wiki.techinc.nl/tests/phpunit/includes/auth/ResetPasswordSecondaryAuthenticationProviderTest.php
Thiemo Kreuz 9e6e3f8d08 Inline a few more trivial createMock() calls in tests
createMock() is still relatively new. This code was using more
complicated mock builders before. This was changed just recently.
createMock() is now so short, the extra helper methods don't make
the code more readable, I would argue.

Change-Id: Ia7e24827157d5f49fc7da102418c79ae33c8e053
2022-08-08 09:50:50 +02:00

333 lines
11 KiB
PHP

<?php
namespace MediaWiki\Auth;
use MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait;
use MediaWiki\User\UserNameUtils;
use Psr\Container\ContainerInterface;
use Wikimedia\ObjectFactory\ObjectFactory;
use Wikimedia\TestingAccessWrapper;
/**
* @group AuthManager
* @covers \MediaWiki\Auth\ResetPasswordSecondaryAuthenticationProvider
*/
class ResetPasswordSecondaryAuthenticationProviderTest extends \MediaWikiIntegrationTestCase {
use AuthenticationProviderTestTrait;
/**
* @dataProvider provideGetAuthenticationRequests
* @param string $action
* @param array $response
*/
public function testGetAuthenticationRequests( $action, $response ) {
$provider = new ResetPasswordSecondaryAuthenticationProvider();
$this->assertEquals( $response, $provider->getAuthenticationRequests( $action, [] ) );
}
public static function provideGetAuthenticationRequests() {
return [
[ AuthManager::ACTION_LOGIN, [] ],
[ AuthManager::ACTION_CREATE, [] ],
[ AuthManager::ACTION_LINK, [] ],
[ AuthManager::ACTION_CHANGE, [] ],
[ AuthManager::ACTION_REMOVE, [] ],
];
}
public function testBasics() {
$user = \User::newFromName( 'UTSysop' );
$user2 = new \User;
$obj = new \stdClass;
$reqs = [ new \stdClass ];
$mb = $this->getMockBuilder( ResetPasswordSecondaryAuthenticationProvider::class )
->onlyMethods( [ 'tryReset' ] );
$methods = [
'beginSecondaryAuthentication' => [ $user, $reqs ],
'continueSecondaryAuthentication' => [ $user, $reqs ],
'beginSecondaryAccountCreation' => [ $user, $user2, $reqs ],
'continueSecondaryAccountCreation' => [ $user, $user2, $reqs ],
];
foreach ( $methods as $method => $args ) {
$mock = $mb->getMock();
$mock->expects( $this->once() )->method( 'tryReset' )
->with( $this->identicalTo( $user ), $this->identicalTo( $reqs ) )
->willReturn( $obj );
$this->assertSame( $obj, $mock->$method( ...$args ) );
}
}
public function testTryReset() {
$user = \User::newFromName( 'UTSysop' );
$provider = $this->getMockBuilder(
ResetPasswordSecondaryAuthenticationProvider::class
)
->onlyMethods( [
'providerAllowsAuthenticationDataChange', 'providerChangeAuthenticationData'
] )
->getMock();
$provider->method( 'providerAllowsAuthenticationDataChange' )
->willReturnCallback( function ( $req ) {
$this->assertSame( 'UTSysop', $req->username );
return $req->allow;
} );
$provider->method( 'providerChangeAuthenticationData' )
->willReturnCallback( function ( $req ) {
$this->assertSame( 'UTSysop', $req->username );
$req->done = true;
} );
$config = new \HashConfig( [
'AuthManagerConfig' => [
'preauth' => [],
'primaryauth' => [],
'secondaryauth' => [
[ 'factory' => static function () use ( $provider ) {
return $provider;
} ],
],
],
] );
$mwServices = $this->getServiceContainer();
$manager = new AuthManager(
new \FauxRequest,
$config,
new ObjectFactory( $this->createNoOpAbstractMock( ContainerInterface::class ) ),
$this->createHookContainer(),
$mwServices->getReadOnlyMode(),
$this->createNoOpMock( UserNameUtils::class ),
$mwServices->getBlockManager(),
$mwServices->getWatchlistManager(),
$mwServices->getDBLoadBalancer(),
$mwServices->getContentLanguage(),
$mwServices->getLanguageConverterFactory(),
$mwServices->getBotPasswordStore(),
$mwServices->getUserFactory(),
$mwServices->getUserIdentityLookup(),
$mwServices->getUserOptionsManager()
);
$this->initProvider( $provider, null, null, $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 = 'Foo';
$passReq->retype = 'Bar';
$passReq->allow = \StatusValue::newGood();
$passReq->done = false;
$passReq2 = $this->getMockBuilder( PasswordAuthenticationRequest::class )
->enableProxyingToOriginalMethods()
->getMock();
$passReq2->action = AuthManager::ACTION_CHANGE;
$passReq2->password = 'Foo';
$passReq2->retype = 'Foo';
$passReq2->allow = \StatusValue::newGood();
$passReq2->done = false;
$passReq3 = new PasswordAuthenticationRequest();
$passReq3->action = AuthManager::ACTION_LOGIN;
$passReq3->password = 'Foo';
$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 );
$expectedPassReq = clone $passReq;
$expectedPassReq->required = AuthenticationRequest::OPTIONAL;
$this->assertEquals( $expectedPassReq, $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 );
$expectedPassReq = clone $passReq2;
$expectedPassReq->required = AuthenticationRequest::OPTIONAL;
$this->assertEquals( $expectedPassReq, $res->neededRequests[0] );
$this->assertEquals( $skipReq, $res->neededRequests[1] );
$this->assertNotNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
$this->assertFalse( $passReq->done );
$this->assertFalse( $passReq2->done );
}
}