3 namespace MediaWiki\Auth
;
5 use Wikimedia\TestingAccessWrapper
;
9 * @covers MediaWiki\Auth\ConfirmLinkSecondaryAuthenticationProvider
11 class ConfirmLinkSecondaryAuthenticationProviderTest
extends \MediaWikiTestCase
{
13 * @dataProvider provideGetAuthenticationRequests
14 * @param string $action
15 * @param array $response
17 public function testGetAuthenticationRequests( $action, $response ) {
18 $provider = new ConfirmLinkSecondaryAuthenticationProvider();
20 $this->assertEquals( $response, $provider->getAuthenticationRequests( $action, [] ) );
23 public static function provideGetAuthenticationRequests() {
25 [ AuthManager
::ACTION_LOGIN
, [] ],
26 [ AuthManager
::ACTION_CREATE
, [] ],
27 [ AuthManager
::ACTION_LINK
, [] ],
28 [ AuthManager
::ACTION_CHANGE
, [] ],
29 [ AuthManager
::ACTION_REMOVE
, [] ],
33 public function testBeginSecondaryAuthentication() {
34 $user = \User
::newFromName( 'UTSysop' );
37 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
38 ->setMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
40 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
41 ->with( $this->identicalTo( $user ), $this->identicalTo( 'AuthManager::authnState' ) )
42 ->will( $this->returnValue( $obj ) );
43 $mock->expects( $this->never() )->method( 'continueLinkAttempt' );
45 $this->assertSame( $obj, $mock->beginSecondaryAuthentication( $user, [] ) );
48 public function testContinueSecondaryAuthentication() {
49 $user = \User
::newFromName( 'UTSysop' );
51 $reqs = [ new \stdClass
];
53 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
54 ->setMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
56 $mock->expects( $this->never() )->method( 'beginLinkAttempt' );
57 $mock->expects( $this->once() )->method( 'continueLinkAttempt' )
59 $this->identicalTo( $user ),
60 $this->identicalTo( 'AuthManager::authnState' ),
61 $this->identicalTo( $reqs )
63 ->will( $this->returnValue( $obj ) );
65 $this->assertSame( $obj, $mock->continueSecondaryAuthentication( $user, $reqs ) );
68 public function testBeginSecondaryAccountCreation() {
69 $user = \User
::newFromName( 'UTSysop' );
72 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
73 ->setMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
75 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
76 ->with( $this->identicalTo( $user ), $this->identicalTo( 'AuthManager::accountCreationState' ) )
77 ->will( $this->returnValue( $obj ) );
78 $mock->expects( $this->never() )->method( 'continueLinkAttempt' );
80 $this->assertSame( $obj, $mock->beginSecondaryAccountCreation( $user, $user, [] ) );
83 public function testContinueSecondaryAccountCreation() {
84 $user = \User
::newFromName( 'UTSysop' );
86 $reqs = [ new \stdClass
];
88 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
89 ->setMethods( [ 'beginLinkAttempt', 'continueLinkAttempt' ] )
91 $mock->expects( $this->never() )->method( 'beginLinkAttempt' );
92 $mock->expects( $this->once() )->method( 'continueLinkAttempt' )
94 $this->identicalTo( $user ),
95 $this->identicalTo( 'AuthManager::accountCreationState' ),
96 $this->identicalTo( $reqs )
98 ->will( $this->returnValue( $obj ) );
100 $this->assertSame( $obj, $mock->continueSecondaryAccountCreation( $user, $user, $reqs ) );
104 * Get requests for testing
105 * @return AuthenticationRequest[]
107 private function getLinkRequests() {
110 $mb = $this->getMockBuilder( AuthenticationRequest
::class )
111 ->setMethods( [ 'getUniqueId' ] );
112 for ( $i = 1; $i <= 3; $i++
) {
113 $req = $mb->getMockForAbstractClass();
114 $req->expects( $this->any() )->method( 'getUniqueId' )
115 ->will( $this->returnValue( "Request$i" ) );
117 $reqs[$req->getUniqueId()] = $req;
123 public function testBeginLinkAttempt() {
124 $badReq = $this->getMockBuilder( AuthenticationRequest
::class )
125 ->setMethods( [ 'getUniqueId' ] )
126 ->getMockForAbstractClass();
127 $badReq->expects( $this->any() )->method( 'getUniqueId' )
128 ->will( $this->returnValue( "BadReq" ) );
130 $user = \User
::newFromName( 'UTSysop' );
131 $provider = TestingAccessWrapper
::newFromObject(
132 new ConfirmLinkSecondaryAuthenticationProvider
134 $request = new \
FauxRequest();
135 $manager = $this->getMockBuilder( AuthManager
::class )
136 ->setMethods( [ 'allowsAuthenticationDataChange' ] )
137 ->setConstructorArgs( [ $request, \RequestContext
::getMain()->getConfig() ] )
139 $manager->expects( $this->any() )->method( 'allowsAuthenticationDataChange' )
140 ->will( $this->returnCallback( function ( $req ) {
141 return $req->getUniqueId() !== 'BadReq'
142 ? \StatusValue
::newGood()
143 : \StatusValue
::newFatal( 'no' );
145 $provider->setManager( $manager );
148 AuthenticationResponse
::newAbstain(),
149 $provider->beginLinkAttempt( $user, 'state' )
152 $request->getSession()->setSecret( 'state', [
156 AuthenticationResponse
::newAbstain(),
157 $provider->beginLinkAttempt( $user, 'state' )
160 $reqs = $this->getLinkRequests();
161 $request->getSession()->setSecret( 'state', [
162 'maybeLink' => $reqs +
[ 'BadReq' => $badReq ]
164 $res = $provider->beginLinkAttempt( $user, 'state' );
165 $this->assertInstanceOf( AuthenticationResponse
::class, $res );
166 $this->assertSame( AuthenticationResponse
::UI
, $res->status
);
167 $this->assertSame( 'authprovider-confirmlink-message', $res->message
->getKey() );
168 $this->assertCount( 1, $res->neededRequests
);
169 $req = $res->neededRequests
[0];
170 $this->assertInstanceOf( ConfirmLinkAuthenticationRequest
::class, $req );
171 $expectReqs = $this->getLinkRequests();
172 foreach ( $expectReqs as $r ) {
173 $r->action
= AuthManager
::ACTION_CHANGE
;
174 $r->username
= $user->getName();
176 $this->assertEquals( $expectReqs, TestingAccessWrapper
::newFromObject( $req )->linkRequests
);
179 public function testContinueLinkAttempt() {
180 $user = \User
::newFromName( 'UTSysop' );
181 $obj = new \stdClass
;
182 $reqs = $this->getLinkRequests();
184 $done = [ false, false, false ];
186 // First, test the pass-through for not containing the ConfirmLinkAuthenticationRequest
187 $mock = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
188 ->setMethods( [ 'beginLinkAttempt' ] )
190 $mock->expects( $this->once() )->method( 'beginLinkAttempt' )
191 ->with( $this->identicalTo( $user ), $this->identicalTo( 'state' ) )
192 ->will( $this->returnValue( $obj ) );
195 TestingAccessWrapper
::newFromObject( $mock )->continueLinkAttempt( $user, 'state', $reqs )
198 // Now test the actual functioning
199 $provider = $this->getMockBuilder( ConfirmLinkSecondaryAuthenticationProvider
::class )
201 'beginLinkAttempt', 'providerAllowsAuthenticationDataChange',
202 'providerChangeAuthenticationData'
205 $provider->expects( $this->never() )->method( 'beginLinkAttempt' );
206 $provider->expects( $this->any() )->method( 'providerAllowsAuthenticationDataChange' )
207 ->will( $this->returnCallback( function ( $req ) use ( $reqs ) {
208 return $req->getUniqueId() === 'Request3'
209 ? \StatusValue
::newFatal( 'foo' ) : \StatusValue
::newGood();
211 $provider->expects( $this->any() )->method( 'providerChangeAuthenticationData' )
212 ->will( $this->returnCallback( function ( $req ) use ( &$done ) {
213 $done[$req->id
] = true;
215 $config = new \
HashConfig( [
216 'AuthManagerConfig' => [
220 [ 'factory' => function () use ( $provider ) {
226 $request = new \
FauxRequest();
227 $manager = new AuthManager( $request, $config );
228 $provider->setManager( $manager );
229 $provider = TestingAccessWrapper
::newFromObject( $provider );
231 $req = new ConfirmLinkAuthenticationRequest( $reqs );
234 AuthenticationResponse
::newAbstain(),
235 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
238 $request->getSession()->setSecret( 'state', [
242 AuthenticationResponse
::newAbstain(),
243 $provider->continueLinkAttempt( $user, 'state', [ $req ] )
246 $request->getSession()->setSecret( 'state', [
250 AuthenticationResponse
::newPass(),
251 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] )
253 $this->assertSame( [ false, false, false ], $done );
255 $request->getSession()->setSecret( 'state', [
256 'maybeLink' => [ $reqs['Request2'] ],
258 $req->confirmedLinkIDs
= [ 'Request1', 'Request2' ];
259 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
260 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
261 $this->assertSame( [ false, true, false ], $done );
262 $done = [ false, false, false ];
264 $request->getSession()->setSecret( 'state', [
265 'maybeLink' => $reqs,
267 $req->confirmedLinkIDs
= [ 'Request1', 'Request2' ];
268 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
269 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
270 $this->assertSame( [ true, true, false ], $done );
271 $done = [ false, false, false ];
273 $request->getSession()->setSecret( 'state', [
274 'maybeLink' => $reqs,
276 $req->confirmedLinkIDs
= [ 'Request1', 'Request3' ];
277 $res = $provider->continueLinkAttempt( $user, 'state', [ $req ] );
278 $this->assertEquals( AuthenticationResponse
::UI
, $res->status
);
279 $this->assertCount( 1, $res->neededRequests
);
280 $this->assertInstanceOf( ButtonAuthenticationRequest
::class, $res->neededRequests
[0] );
281 $this->assertSame( [ true, false, false ], $done );
282 $done = [ false, false, false ];
284 $res = $provider->continueLinkAttempt( $user, 'state', [ $res->neededRequests
[0] ] );
285 $this->assertEquals( AuthenticationResponse
::newPass(), $res );
286 $this->assertSame( [ false, false, false ], $done );