Instead of having basically every caller do: $pf = new PasswordFactory(); $pf->init( RequestContext::getMain()->getConfig() ); Just create a single PasswordFactory via MediaWikiServices and pass that around. Things that want to use their own config can still pass settings via the new constructor. This will eventually let us remove the init() function, removing the only hard dependency upon MediaWiki, to make it easier to librarize (T89742). Change-Id: I0fc7520dc023b11a7fa66083eff7b88ebfe49c7b
719 lines
23 KiB
PHP
719 lines
23 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\Auth;
|
|
|
|
use MediaWiki\MediaWikiServices;
|
|
use Wikimedia\ScopedCallback;
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* @group AuthManager
|
|
* @group Database
|
|
* @covers MediaWiki\Auth\TemporaryPasswordPrimaryAuthenticationProvider
|
|
*/
|
|
class TemporaryPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase {
|
|
|
|
private $manager = null;
|
|
private $config = null;
|
|
private $validity = null;
|
|
|
|
/**
|
|
* Get an instance of the provider
|
|
*
|
|
* $provider->checkPasswordValidity is mocked to return $this->validity,
|
|
* because we don't need to test that here.
|
|
*
|
|
* @param array $params
|
|
* @return TemporaryPasswordPrimaryAuthenticationProvider
|
|
*/
|
|
protected function getProvider( $params = [] ) {
|
|
if ( !$this->config ) {
|
|
$this->config = new \HashConfig( [
|
|
'EmailEnabled' => true,
|
|
] );
|
|
}
|
|
$config = new \MultiConfig( [
|
|
$this->config,
|
|
MediaWikiServices::getInstance()->getMainConfig()
|
|
] );
|
|
|
|
if ( !$this->manager ) {
|
|
$this->manager = new AuthManager( new \FauxRequest(), $config );
|
|
}
|
|
$this->validity = \Status::newGood();
|
|
|
|
$mockedMethods[] = 'checkPasswordValidity';
|
|
$provider = $this->getMockBuilder( TemporaryPasswordPrimaryAuthenticationProvider::class )
|
|
->setMethods( $mockedMethods )
|
|
->setConstructorArgs( [ $params ] )
|
|
->getMock();
|
|
$provider->expects( $this->any() )->method( 'checkPasswordValidity' )
|
|
->will( $this->returnCallback( function () {
|
|
return $this->validity;
|
|
} ) );
|
|
$provider->setConfig( $config );
|
|
$provider->setLogger( new \Psr\Log\NullLogger() );
|
|
$provider->setManager( $this->manager );
|
|
|
|
return $provider;
|
|
}
|
|
|
|
protected function hookMailer( $func = null ) {
|
|
\Hooks::clear( 'AlternateUserMailer' );
|
|
if ( $func ) {
|
|
\Hooks::register( 'AlternateUserMailer', $func );
|
|
// Safety
|
|
\Hooks::register( 'AlternateUserMailer', function () {
|
|
return false;
|
|
} );
|
|
} else {
|
|
\Hooks::register( 'AlternateUserMailer', function () {
|
|
$this->fail( 'AlternateUserMailer hook called unexpectedly' );
|
|
return false;
|
|
} );
|
|
}
|
|
|
|
return new ScopedCallback( function () {
|
|
\Hooks::clear( 'AlternateUserMailer' );
|
|
\Hooks::register( 'AlternateUserMailer', function () {
|
|
return false;
|
|
} );
|
|
} );
|
|
}
|
|
|
|
public function testBasics() {
|
|
$provider = new TemporaryPasswordPrimaryAuthenticationProvider();
|
|
|
|
$this->assertSame(
|
|
PrimaryAuthenticationProvider::TYPE_CREATE,
|
|
$provider->accountCreationType()
|
|
);
|
|
|
|
$this->assertTrue( $provider->testUserExists( 'UTSysop' ) );
|
|
$this->assertTrue( $provider->testUserExists( 'uTSysop' ) );
|
|
$this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
|
|
$this->assertFalse( $provider->testUserExists( '<invalid>' ) );
|
|
|
|
$req = new PasswordAuthenticationRequest;
|
|
$req->action = AuthManager::ACTION_CHANGE;
|
|
$req->username = '<invalid>';
|
|
$provider->providerChangeAuthenticationData( $req );
|
|
}
|
|
|
|
public function testConfig() {
|
|
$config = new \HashConfig( [
|
|
'EnableEmail' => false,
|
|
'NewPasswordExpiry' => 100,
|
|
'PasswordReminderResendTime' => 101,
|
|
] );
|
|
|
|
$p = TestingAccessWrapper::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider() );
|
|
$p->setConfig( $config );
|
|
$this->assertSame( false, $p->emailEnabled );
|
|
$this->assertSame( 100, $p->newPasswordExpiry );
|
|
$this->assertSame( 101, $p->passwordReminderResendTime );
|
|
|
|
$p = TestingAccessWrapper::newFromObject( new TemporaryPasswordPrimaryAuthenticationProvider( [
|
|
'emailEnabled' => true,
|
|
'newPasswordExpiry' => 42,
|
|
'passwordReminderResendTime' => 43,
|
|
] ) );
|
|
$p->setConfig( $config );
|
|
$this->assertSame( true, $p->emailEnabled );
|
|
$this->assertSame( 42, $p->newPasswordExpiry );
|
|
$this->assertSame( 43, $p->passwordReminderResendTime );
|
|
}
|
|
|
|
public function testTestUserCanAuthenticate() {
|
|
$user = self::getMutableTestUser()->getUser();
|
|
|
|
$dbw = wfGetDB( DB_MASTER );
|
|
$config = MediaWikiServices::getInstance()->getMainConfig();
|
|
// A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
|
|
$passwordFactory = new \PasswordFactory( $config->get( 'PasswordConfig' ), 'A' );
|
|
|
|
$pwhash = $passwordFactory->newFromPlaintext( 'password' )->toString();
|
|
|
|
$provider = $this->getProvider();
|
|
$providerPriv = TestingAccessWrapper::newFromObject( $provider );
|
|
|
|
$this->assertFalse( $provider->testUserCanAuthenticate( '<invalid>' ) );
|
|
$this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
|
|
|
|
$dbw->update(
|
|
'user',
|
|
[
|
|
'user_newpassword' => \PasswordFactory::newInvalidPassword()->toString(),
|
|
'user_newpass_time' => null,
|
|
],
|
|
[ 'user_id' => $user->getId() ]
|
|
);
|
|
$this->assertFalse( $provider->testUserCanAuthenticate( $user->getName() ) );
|
|
|
|
$dbw->update(
|
|
'user',
|
|
[
|
|
'user_newpassword' => $pwhash,
|
|
'user_newpass_time' => null,
|
|
],
|
|
[ 'user_id' => $user->getId() ]
|
|
);
|
|
$this->assertTrue( $provider->testUserCanAuthenticate( $user->getName() ) );
|
|
$this->assertTrue( $provider->testUserCanAuthenticate( lcfirst( $user->getName() ) ) );
|
|
|
|
$dbw->update(
|
|
'user',
|
|
[
|
|
'user_newpassword' => $pwhash,
|
|
'user_newpass_time' => $dbw->timestamp( time() - 10 ),
|
|
],
|
|
[ 'user_id' => $user->getId() ]
|
|
);
|
|
$providerPriv->newPasswordExpiry = 100;
|
|
$this->assertTrue( $provider->testUserCanAuthenticate( $user->getName() ) );
|
|
$providerPriv->newPasswordExpiry = 1;
|
|
$this->assertFalse( $provider->testUserCanAuthenticate( $user->getName() ) );
|
|
|
|
$dbw->update(
|
|
'user',
|
|
[
|
|
'user_newpassword' => \PasswordFactory::newInvalidPassword()->toString(),
|
|
'user_newpass_time' => null,
|
|
],
|
|
[ 'user_id' => $user->getId() ]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideGetAuthenticationRequests
|
|
* @param string $action
|
|
* @param array $options
|
|
* @param array $expected
|
|
*/
|
|
public function testGetAuthenticationRequests( $action, $options, $expected ) {
|
|
$actual = $this->getProvider()->getAuthenticationRequests( $action, $options );
|
|
foreach ( $actual as $req ) {
|
|
if ( $req instanceof TemporaryPasswordAuthenticationRequest && $req->password !== null ) {
|
|
$req->password = 'random';
|
|
}
|
|
}
|
|
$this->assertEquals( $expected, $actual );
|
|
}
|
|
|
|
public static function provideGetAuthenticationRequests() {
|
|
$anon = [ 'username' => null ];
|
|
$loggedIn = [ 'username' => 'UTSysop' ];
|
|
|
|
return [
|
|
[ AuthManager::ACTION_LOGIN, $anon, [
|
|
new PasswordAuthenticationRequest
|
|
] ],
|
|
[ AuthManager::ACTION_LOGIN, $loggedIn, [
|
|
new PasswordAuthenticationRequest
|
|
] ],
|
|
[ AuthManager::ACTION_CREATE, $anon, [] ],
|
|
[ AuthManager::ACTION_CREATE, $loggedIn, [
|
|
new TemporaryPasswordAuthenticationRequest( 'random' )
|
|
] ],
|
|
[ AuthManager::ACTION_LINK, $anon, [] ],
|
|
[ AuthManager::ACTION_LINK, $loggedIn, [] ],
|
|
[ AuthManager::ACTION_CHANGE, $anon, [
|
|
new TemporaryPasswordAuthenticationRequest( 'random' )
|
|
] ],
|
|
[ AuthManager::ACTION_CHANGE, $loggedIn, [
|
|
new TemporaryPasswordAuthenticationRequest( 'random' )
|
|
] ],
|
|
[ AuthManager::ACTION_REMOVE, $anon, [
|
|
new TemporaryPasswordAuthenticationRequest
|
|
] ],
|
|
[ AuthManager::ACTION_REMOVE, $loggedIn, [
|
|
new TemporaryPasswordAuthenticationRequest
|
|
] ],
|
|
];
|
|
}
|
|
|
|
public function testAuthentication() {
|
|
$user = self::getMutableTestUser()->getUser();
|
|
|
|
$password = 'TemporaryPassword';
|
|
$hash = ':A:' . md5( $password );
|
|
$dbw = wfGetDB( DB_MASTER );
|
|
$dbw->update(
|
|
'user',
|
|
[ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() - 10 ) ],
|
|
[ 'user_id' => $user->getId() ]
|
|
);
|
|
|
|
$req = new PasswordAuthenticationRequest();
|
|
$req->action = AuthManager::ACTION_LOGIN;
|
|
$reqs = [ PasswordAuthenticationRequest::class => $req ];
|
|
|
|
$provider = $this->getProvider();
|
|
$providerPriv = TestingAccessWrapper::newFromObject( $provider );
|
|
|
|
$providerPriv->newPasswordExpiry = 100;
|
|
|
|
// General failures
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newAbstain(),
|
|
$provider->beginPrimaryAuthentication( [] )
|
|
);
|
|
|
|
$req->username = 'foo';
|
|
$req->password = null;
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newAbstain(),
|
|
$provider->beginPrimaryAuthentication( $reqs )
|
|
);
|
|
|
|
$req->username = null;
|
|
$req->password = 'bar';
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newAbstain(),
|
|
$provider->beginPrimaryAuthentication( $reqs )
|
|
);
|
|
|
|
$req->username = '<invalid>';
|
|
$req->password = 'WhoCares';
|
|
$ret = $provider->beginPrimaryAuthentication( $reqs );
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newAbstain(),
|
|
$provider->beginPrimaryAuthentication( $reqs )
|
|
);
|
|
|
|
$req->username = 'DoesNotExist';
|
|
$req->password = 'DoesNotExist';
|
|
$ret = $provider->beginPrimaryAuthentication( $reqs );
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newAbstain(),
|
|
$provider->beginPrimaryAuthentication( $reqs )
|
|
);
|
|
|
|
// Validation failure
|
|
$req->username = $user->getName();
|
|
$req->password = $password;
|
|
$this->validity = \Status::newFatal( 'arbitrary-failure' );
|
|
$ret = $provider->beginPrimaryAuthentication( $reqs );
|
|
$this->assertEquals(
|
|
AuthenticationResponse::FAIL,
|
|
$ret->status
|
|
);
|
|
$this->assertEquals(
|
|
'arbitrary-failure',
|
|
$ret->message->getKey()
|
|
);
|
|
|
|
// Successful auth
|
|
$this->manager->removeAuthenticationSessionData( null );
|
|
$this->validity = \Status::newGood();
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newPass( $user->getName() ),
|
|
$provider->beginPrimaryAuthentication( $reqs )
|
|
);
|
|
$this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
|
|
|
|
$this->manager->removeAuthenticationSessionData( null );
|
|
$this->validity = \Status::newGood();
|
|
$req->username = lcfirst( $user->getName() );
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newPass( $user->getName() ),
|
|
$provider->beginPrimaryAuthentication( $reqs )
|
|
);
|
|
$this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
|
|
$req->username = $user->getName();
|
|
|
|
// Expired password
|
|
$providerPriv->newPasswordExpiry = 1;
|
|
$ret = $provider->beginPrimaryAuthentication( $reqs );
|
|
$this->assertEquals(
|
|
AuthenticationResponse::FAIL,
|
|
$ret->status
|
|
);
|
|
$this->assertEquals(
|
|
'wrongpassword',
|
|
$ret->message->getKey()
|
|
);
|
|
|
|
// Bad password
|
|
$providerPriv->newPasswordExpiry = 100;
|
|
$this->validity = \Status::newGood();
|
|
$req->password = 'Wrong';
|
|
$ret = $provider->beginPrimaryAuthentication( $reqs );
|
|
$this->assertEquals(
|
|
AuthenticationResponse::FAIL,
|
|
$ret->status
|
|
);
|
|
$this->assertEquals(
|
|
'wrongpassword',
|
|
$ret->message->getKey()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideProviderAllowsAuthenticationDataChange
|
|
* @param string $type
|
|
* @param string $user
|
|
* @param \Status $validity Result of the password validity check
|
|
* @param \StatusValue $expect1 Expected result with $checkData = false
|
|
* @param \StatusValue $expect2 Expected result with $checkData = true
|
|
*/
|
|
public function testProviderAllowsAuthenticationDataChange( $type, $user, \Status $validity,
|
|
\StatusValue $expect1, \StatusValue $expect2
|
|
) {
|
|
if ( $type === PasswordAuthenticationRequest::class ||
|
|
$type === TemporaryPasswordAuthenticationRequest::class
|
|
) {
|
|
$req = new $type();
|
|
} else {
|
|
$req = $this->createMock( $type );
|
|
}
|
|
$req->action = AuthManager::ACTION_CHANGE;
|
|
$req->username = $user;
|
|
$req->password = 'NewPassword';
|
|
|
|
$provider = $this->getProvider();
|
|
$this->validity = $validity;
|
|
$this->assertEquals( $expect1, $provider->providerAllowsAuthenticationDataChange( $req, false ) );
|
|
$this->assertEquals( $expect2, $provider->providerAllowsAuthenticationDataChange( $req, true ) );
|
|
}
|
|
|
|
public static function provideProviderAllowsAuthenticationDataChange() {
|
|
$err = \StatusValue::newGood();
|
|
$err->error( 'arbitrary-warning' );
|
|
|
|
return [
|
|
[ AuthenticationRequest::class, 'UTSysop', \Status::newGood(),
|
|
\StatusValue::newGood( 'ignored' ), \StatusValue::newGood( 'ignored' ) ],
|
|
[ PasswordAuthenticationRequest::class, 'UTSysop', \Status::newGood(),
|
|
\StatusValue::newGood( 'ignored' ), \StatusValue::newGood( 'ignored' ) ],
|
|
[ TemporaryPasswordAuthenticationRequest::class, 'UTSysop', \Status::newGood(),
|
|
\StatusValue::newGood(), \StatusValue::newGood() ],
|
|
[ TemporaryPasswordAuthenticationRequest::class, 'uTSysop', \Status::newGood(),
|
|
\StatusValue::newGood(), \StatusValue::newGood() ],
|
|
[ TemporaryPasswordAuthenticationRequest::class, 'UTSysop', \Status::wrap( $err ),
|
|
\StatusValue::newGood(), $err ],
|
|
[ TemporaryPasswordAuthenticationRequest::class, 'UTSysop',
|
|
\Status::newFatal( 'arbitrary-error' ), \StatusValue::newGood(),
|
|
\StatusValue::newFatal( 'arbitrary-error' ) ],
|
|
[ TemporaryPasswordAuthenticationRequest::class, 'DoesNotExist', \Status::newGood(),
|
|
\StatusValue::newGood(), \StatusValue::newGood( 'ignored' ) ],
|
|
[ TemporaryPasswordAuthenticationRequest::class, '<invalid>', \Status::newGood(),
|
|
\StatusValue::newGood(), \StatusValue::newGood( 'ignored' ) ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideProviderChangeAuthenticationData
|
|
* @param string $user
|
|
* @param string $type
|
|
* @param bool $changed
|
|
*/
|
|
public function testProviderChangeAuthenticationData( $user, $type, $changed ) {
|
|
$cuser = ucfirst( $user );
|
|
$oldpass = 'OldTempPassword';
|
|
$newpass = 'NewTempPassword';
|
|
|
|
$dbw = wfGetDB( DB_MASTER );
|
|
$oldHash = $dbw->selectField( 'user', 'user_newpassword', [ 'user_name' => $cuser ] );
|
|
$cb = new ScopedCallback( function () use ( $dbw, $cuser, $oldHash ) {
|
|
$dbw->update( 'user', [ 'user_newpassword' => $oldHash ], [ 'user_name' => $cuser ] );
|
|
} );
|
|
|
|
$hash = ':A:' . md5( $oldpass );
|
|
$dbw->update(
|
|
'user',
|
|
[ 'user_newpassword' => $hash, 'user_newpass_time' => $dbw->timestamp( time() + 10 ) ],
|
|
[ 'user_name' => $cuser ]
|
|
);
|
|
|
|
$provider = $this->getProvider();
|
|
|
|
// Sanity check
|
|
$loginReq = new PasswordAuthenticationRequest();
|
|
$loginReq->action = AuthManager::ACTION_CHANGE;
|
|
$loginReq->username = $user;
|
|
$loginReq->password = $oldpass;
|
|
$loginReqs = [ PasswordAuthenticationRequest::class => $loginReq ];
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newPass( $cuser ),
|
|
$provider->beginPrimaryAuthentication( $loginReqs ),
|
|
'Sanity check'
|
|
);
|
|
|
|
if ( $type === PasswordAuthenticationRequest::class ||
|
|
$type === TemporaryPasswordAuthenticationRequest::class
|
|
) {
|
|
$changeReq = new $type();
|
|
} else {
|
|
$changeReq = $this->createMock( $type );
|
|
}
|
|
$changeReq->action = AuthManager::ACTION_CHANGE;
|
|
$changeReq->username = $user;
|
|
$changeReq->password = $newpass;
|
|
$resetMailer = $this->hookMailer();
|
|
$provider->providerChangeAuthenticationData( $changeReq );
|
|
ScopedCallback::consume( $resetMailer );
|
|
|
|
$loginReq->password = $oldpass;
|
|
$ret = $provider->beginPrimaryAuthentication( $loginReqs );
|
|
$this->assertEquals(
|
|
AuthenticationResponse::FAIL,
|
|
$ret->status,
|
|
'old password should fail'
|
|
);
|
|
$this->assertEquals(
|
|
'wrongpassword',
|
|
$ret->message->getKey(),
|
|
'old password should fail'
|
|
);
|
|
|
|
$loginReq->password = $newpass;
|
|
$ret = $provider->beginPrimaryAuthentication( $loginReqs );
|
|
if ( $changed ) {
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newPass( $cuser ),
|
|
$ret,
|
|
'new password should pass'
|
|
);
|
|
$this->assertNotNull(
|
|
$dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
|
|
);
|
|
} else {
|
|
$this->assertEquals(
|
|
AuthenticationResponse::FAIL,
|
|
$ret->status,
|
|
'new password should fail'
|
|
);
|
|
$this->assertEquals(
|
|
'wrongpassword',
|
|
$ret->message->getKey(),
|
|
'new password should fail'
|
|
);
|
|
$this->assertNull(
|
|
$dbw->selectField( 'user', 'user_newpass_time', [ 'user_name' => $cuser ] )
|
|
);
|
|
}
|
|
}
|
|
|
|
public static function provideProviderChangeAuthenticationData() {
|
|
return [
|
|
[ 'UTSysop', AuthenticationRequest::class, false ],
|
|
[ 'UTSysop', PasswordAuthenticationRequest::class, false ],
|
|
[ 'UTSysop', TemporaryPasswordAuthenticationRequest::class, true ],
|
|
];
|
|
}
|
|
|
|
public function testProviderChangeAuthenticationDataEmail() {
|
|
$user = self::getMutableTestUser()->getUser();
|
|
|
|
$dbw = wfGetDB( DB_MASTER );
|
|
$dbw->update(
|
|
'user',
|
|
[ 'user_newpass_time' => $dbw->timestamp( time() - 5 * 3600 ) ],
|
|
[ 'user_id' => $user->getId() ]
|
|
);
|
|
|
|
$req = TemporaryPasswordAuthenticationRequest::newRandom();
|
|
$req->username = $user->getName();
|
|
$req->mailpassword = true;
|
|
|
|
$provider = $this->getProvider( [ 'emailEnabled' => false ] );
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertEquals( \StatusValue::newFatal( 'passwordreset-emaildisabled' ), $status );
|
|
|
|
$provider = $this->getProvider( [ 'passwordReminderResendTime' => 10 ] );
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertEquals( \StatusValue::newFatal( 'throttled-mailpassword', 10 ), $status );
|
|
|
|
$provider = $this->getProvider( [ 'passwordReminderResendTime' => 3 ] );
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
|
|
|
|
$dbw->update(
|
|
'user',
|
|
[ 'user_newpass_time' => $dbw->timestamp( time() + 5 * 3600 ) ],
|
|
[ 'user_id' => $user->getId() ]
|
|
);
|
|
$provider = $this->getProvider( [ 'passwordReminderResendTime' => 0 ] );
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertFalse( $status->hasMessage( 'throttled-mailpassword' ) );
|
|
|
|
$req->caller = null;
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertEquals( \StatusValue::newFatal( 'passwordreset-nocaller' ), $status );
|
|
|
|
$req->caller = '127.0.0.256';
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertEquals( \StatusValue::newFatal( 'passwordreset-nosuchcaller', '127.0.0.256' ),
|
|
$status );
|
|
|
|
$req->caller = '<Invalid>';
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertEquals( \StatusValue::newFatal( 'passwordreset-nosuchcaller', '<Invalid>' ),
|
|
$status );
|
|
|
|
$req->caller = '127.0.0.1';
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertEquals( \StatusValue::newGood(), $status );
|
|
|
|
$req->caller = $user->getName();
|
|
$status = $provider->providerAllowsAuthenticationDataChange( $req, true );
|
|
$this->assertEquals( \StatusValue::newGood(), $status );
|
|
|
|
$mailed = false;
|
|
$resetMailer = $this->hookMailer( function ( $headers, $to, $from, $subject, $body )
|
|
use ( &$mailed, $req, $user )
|
|
{
|
|
$mailed = true;
|
|
$this->assertSame( $user->getEmail(), $to[0]->address );
|
|
$this->assertContains( $req->password, $body );
|
|
return false;
|
|
} );
|
|
$provider->providerChangeAuthenticationData( $req );
|
|
ScopedCallback::consume( $resetMailer );
|
|
$this->assertTrue( $mailed );
|
|
|
|
$priv = TestingAccessWrapper::newFromObject( $provider );
|
|
$req->username = '<invalid>';
|
|
$status = $priv->sendPasswordResetEmail( $req );
|
|
$this->assertEquals( \Status::newFatal( 'noname' ), $status );
|
|
}
|
|
|
|
public function testTestForAccountCreation() {
|
|
$user = \User::newFromName( 'foo' );
|
|
$req = new TemporaryPasswordAuthenticationRequest();
|
|
$req->username = 'Foo';
|
|
$req->password = 'Bar';
|
|
$reqs = [ TemporaryPasswordAuthenticationRequest::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'
|
|
);
|
|
|
|
$this->validity->error( 'arbitrary warning' );
|
|
$expect = \StatusValue::newGood();
|
|
$expect->error( 'arbitrary warning' );
|
|
$this->assertEquals(
|
|
$expect,
|
|
$provider->testForAccountCreation( $user, $user, $reqs ),
|
|
'Password request, not validated'
|
|
);
|
|
}
|
|
|
|
public function testAccountCreation() {
|
|
$resetMailer = $this->hookMailer();
|
|
|
|
$user = \User::newFromName( 'Foo' );
|
|
|
|
$req = new TemporaryPasswordAuthenticationRequest();
|
|
$reqs = [ TemporaryPasswordAuthenticationRequest::class => $req ];
|
|
|
|
$authreq = new PasswordAuthenticationRequest();
|
|
$authreq->action = AuthManager::ACTION_CREATE;
|
|
$authreqs = [ PasswordAuthenticationRequest::class => $authreq ];
|
|
|
|
$provider = $this->getProvider();
|
|
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newAbstain(),
|
|
$provider->beginPrimaryAccountCreation( $user, $user, [] )
|
|
);
|
|
|
|
$req->username = 'foo';
|
|
$req->password = null;
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newAbstain(),
|
|
$provider->beginPrimaryAccountCreation( $user, $user, $reqs )
|
|
);
|
|
|
|
$req->username = null;
|
|
$req->password = 'bar';
|
|
$this->assertEquals(
|
|
AuthenticationResponse::newAbstain(),
|
|
$provider->beginPrimaryAccountCreation( $user, $user, $reqs )
|
|
);
|
|
|
|
$req->username = 'foo';
|
|
$req->password = 'bar';
|
|
|
|
$expect = AuthenticationResponse::newPass( 'Foo' );
|
|
$expect->createRequest = clone $req;
|
|
$expect->createRequest->username = 'Foo';
|
|
$this->assertEquals( $expect, $provider->beginPrimaryAccountCreation( $user, $user, $reqs ) );
|
|
$this->assertNull( $this->manager->getAuthenticationSessionData( 'no-email' ) );
|
|
|
|
$user = self::getMutableTestUser()->getUser();
|
|
$req->username = $authreq->username = $user->getName();
|
|
$req->password = $authreq->password = 'NewPassword';
|
|
$expect = AuthenticationResponse::newPass( $user->getName() );
|
|
$expect->createRequest = $req;
|
|
|
|
$res2 = $provider->beginPrimaryAccountCreation( $user, $user, $reqs );
|
|
$this->assertEquals( $expect, $res2, 'Sanity check' );
|
|
|
|
$ret = $provider->beginPrimaryAuthentication( $authreqs );
|
|
$this->assertEquals( AuthenticationResponse::FAIL, $ret->status, 'sanity check' );
|
|
|
|
$this->assertSame( null, $provider->finishAccountCreation( $user, $user, $res2 ) );
|
|
|
|
$ret = $provider->beginPrimaryAuthentication( $authreqs );
|
|
$this->assertEquals( AuthenticationResponse::PASS, $ret->status, 'new password is set' );
|
|
}
|
|
|
|
public function testAccountCreationEmail() {
|
|
$creator = \User::newFromName( 'Foo' );
|
|
|
|
$user = self::getMutableTestUser()->getUser();
|
|
$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 );
|
|
|
|
$provider = $this->getProvider( [ 'emailEnabled' => true ] );
|
|
$status = $provider->testForAccountCreation( $user, $creator, [ $req ] );
|
|
$this->assertEquals( \StatusValue::newFatal( 'noemailcreate' ), $status );
|
|
|
|
$user->setEmail( 'test@localhost.localdomain' );
|
|
$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( 'test@localhost.localdomain', $to[0]->address );
|
|
$this->assertContains( $req->password, $body );
|
|
return false;
|
|
} );
|
|
|
|
$expect = AuthenticationResponse::newPass( $user->getName() );
|
|
$expect->createRequest = clone $req;
|
|
$expect->createRequest->username = $user->getName();
|
|
$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 );
|
|
}
|
|
|
|
}
|