3 namespace MediaWiki\Tests\Auth
;
5 use MediaWiki\Auth\AbstractPasswordPrimaryAuthenticationProvider
;
6 use MediaWiki\Auth\AuthenticationResponse
;
7 use MediaWiki\Auth\AuthManager
;
8 use MediaWiki\Auth\PasswordAuthenticationRequest
;
9 use MediaWiki\Config\HashConfig
;
10 use MediaWiki\Config\MultiConfig
;
11 use MediaWiki\MainConfigNames
;
12 use MediaWiki\Password\Password
;
13 use MediaWiki\Password\PasswordFactory
;
14 use MediaWiki\Request\FauxRequest
;
15 use MediaWiki\Status\Status
;
16 use MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait
;
17 use MediaWiki\User\User
;
18 use MediaWiki\User\UserFactory
;
19 use MediaWikiIntegrationTestCase
;
20 use Wikimedia\TestingAccessWrapper
;
24 * @covers \MediaWiki\Auth\AbstractPasswordPrimaryAuthenticationProvider
26 class AbstractPasswordPrimaryAuthenticationProviderTest
extends MediaWikiIntegrationTestCase
{
27 use AuthenticationProviderTestTrait
;
29 public function testConstructor() {
30 $provider = $this->getMockForAbstractClass(
31 AbstractPasswordPrimaryAuthenticationProvider
::class
33 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
34 $this->assertTrue( $providerPriv->authoritative
);
36 $provider = $this->getMockForAbstractClass(
37 AbstractPasswordPrimaryAuthenticationProvider
::class,
38 [ [ 'authoritative' => false ] ]
40 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
41 $this->assertFalse( $providerPriv->authoritative
);
44 public function testGetPasswordFactory() {
45 $provider = $this->getMockForAbstractClass(
46 AbstractPasswordPrimaryAuthenticationProvider
::class
48 $this->initProvider( $provider, $this->getServiceContainer()->getMainConfig() );
49 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
51 $obj = $providerPriv->getPasswordFactory();
52 $this->assertInstanceOf( PasswordFactory
::class, $obj );
53 $this->assertSame( $obj, $providerPriv->getPasswordFactory() );
56 public function testGetPassword() {
57 $provider = $this->getMockForAbstractClass(
58 AbstractPasswordPrimaryAuthenticationProvider
::class
60 $this->initProvider( $provider, $this->getServiceContainer()->getMainConfig() );
61 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
63 $obj = $providerPriv->getPassword( null );
64 $this->assertInstanceOf( Password
::class, $obj );
66 $obj = $providerPriv->getPassword( 'invalid' );
67 $this->assertInstanceOf( Password
::class, $obj );
70 public function testGetNewPasswordExpiry() {
71 $userName = 'TestGetNewPasswordExpiry';
72 $config = new HashConfig
;
73 $provider = $this->getMockForAbstractClass(
74 AbstractPasswordPrimaryAuthenticationProvider
::class
76 $this->initProvider( $provider, new MultiConfig( [ $config, $this->getServiceContainer()->getMainConfig() ] ) );
77 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
79 $config->set( MainConfigNames
::PasswordExpirationDays
, 0 );
80 $this->assertNull( $providerPriv->getNewPasswordExpiry( $userName ) );
82 $config->set( MainConfigNames
::PasswordExpirationDays
, 5 );
83 $this->assertEqualsWithDelta(
85 wfTimestamp( TS_UNIX
, $providerPriv->getNewPasswordExpiry( $userName ) ),
91 new MultiConfig( [ $config, $this->getServiceContainer()->getMainConfig() ] ),
94 $this->createHookContainer( [
95 'ResetPasswordExpiration' => function ( $user, &$expires ) use ( $userName ) {
96 $this->assertSame( $userName, $user->getName() );
97 $expires = '30001231235959';
101 $this->assertSame( '30001231235959', $providerPriv->getNewPasswordExpiry( $userName ) );
104 public function testCheckPasswordValidity() {
106 $uppStatus = Status
::newGood( [] );
107 $this->overrideConfigValue(
108 MainConfigNames
::PasswordPolicy
,
116 'Check' => static function () use ( &$uppCalled, &$uppStatus ) {
123 $this->clearHook( 'PasswordPoliciesForUser' );
125 $provider = $this->getMockForAbstractClass(
126 AbstractPasswordPrimaryAuthenticationProvider
::class
128 $this->initProvider( $provider, $this->getServiceContainer()->getMainConfig() );
129 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
131 $username = '127.0.0.1';
133 $anon->setName( $username );
134 $userFactory = $this->createMock( UserFactory
::class );
135 $userFactory->method( 'newFromName' )->with( $username )->willReturn( $anon );
136 $this->setService( 'UserFactory', $userFactory );
138 $this->assertEquals( $uppStatus, $providerPriv->checkPasswordValidity( $username, 'bar' ) );
140 $uppStatus->fatal( 'arbitrary-warning' );
141 $this->assertEquals( $uppStatus, $providerPriv->checkPasswordValidity( $username, 'bar' ) );
144 public function testSetPasswordResetFlag() {
145 $config = new HashConfig( [
146 MainConfigNames
::InvalidPasswordReset
=> true,
149 $services = $this->getServiceContainer();
150 $manager = new AuthManager(
152 $services->getMainConfig(),
153 $services->getObjectFactory(),
154 $services->getHookContainer(),
155 $services->getReadOnlyMode(),
156 $services->getUserNameUtils(),
157 $services->getBlockManager(),
158 $services->getWatchlistManager(),
159 $services->getDBLoadBalancer(),
160 $services->getContentLanguage(),
161 $services->getLanguageConverterFactory(),
162 $services->getBotPasswordStore(),
163 $services->getUserFactory(),
164 $services->getUserIdentityLookup(),
165 $services->getUserOptionsManager()
168 $provider = $this->getMockForAbstractClass(
169 AbstractPasswordPrimaryAuthenticationProvider
::class
171 $this->initProvider( $provider, $config, null, $manager, $services->getHookContainer() );
172 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
174 $manager->removeAuthenticationSessionData( null );
175 $status = Status
::newGood();
176 $providerPriv->setPasswordResetFlag( 'Foo', $status );
177 $this->assertNull( $manager->getAuthenticationSessionData( 'reset-pass' ) );
179 $manager->removeAuthenticationSessionData( null );
180 $status = Status
::newGood( [ 'suggestChangeOnLogin' => true ] );
181 $status->error( 'testing' );
182 $providerPriv->setPasswordResetFlag( 'Foo', $status );
183 $ret = $manager->getAuthenticationSessionData( 'reset-pass' );
184 $this->assertNotNull( $ret );
185 $this->assertSame( 'resetpass-validity-soft', $ret->msg
->getKey() );
186 $this->assertFalse( $ret->hard
);
188 $config->set( MainConfigNames
::InvalidPasswordReset
, false );
189 $manager->removeAuthenticationSessionData( null );
190 $providerPriv->setPasswordResetFlag( 'Foo', $status );
191 $ret = $manager->getAuthenticationSessionData( 'reset-pass' );
192 $this->assertNull( $ret );
195 public function testFailResponse() {
196 $provider = $this->getMockForAbstractClass(
197 AbstractPasswordPrimaryAuthenticationProvider
::class,
198 [ [ 'authoritative' => false ] ]
200 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
202 $req = new PasswordAuthenticationRequest
;
204 $ret = $providerPriv->failResponse( $req );
205 $this->assertSame( AuthenticationResponse
::ABSTAIN
, $ret->status
);
207 $provider = $this->getMockForAbstractClass(
208 AbstractPasswordPrimaryAuthenticationProvider
::class,
209 [ [ 'authoritative' => true ] ]
211 $providerPriv = TestingAccessWrapper
::newFromObject( $provider );
214 $ret = $providerPriv->failResponse( $req );
215 $this->assertSame( AuthenticationResponse
::FAIL
, $ret->status
);
216 $this->assertSame( 'wrongpasswordempty', $ret->message
->getKey() );
218 $req->password
= 'X';
219 $ret = $providerPriv->failResponse( $req );
220 $this->assertSame( AuthenticationResponse
::FAIL
, $ret->status
);
221 $this->assertSame( 'wrongpassword', $ret->message
->getKey() );
225 * @dataProvider provideGetAuthenticationRequests
226 * @param string $action
227 * @param array $response
229 public function testGetAuthenticationRequests( $action, $response ) {
230 $provider = $this->getMockForAbstractClass(
231 AbstractPasswordPrimaryAuthenticationProvider
::class
234 $this->assertEquals( $response, $provider->getAuthenticationRequests( $action, [] ) );
237 public static function provideGetAuthenticationRequests() {
239 [ AuthManager
::ACTION_LOGIN
, [ new PasswordAuthenticationRequest() ] ],
240 [ AuthManager
::ACTION_CREATE
, [ new PasswordAuthenticationRequest() ] ],
241 [ AuthManager
::ACTION_LINK
, [] ],
242 [ AuthManager
::ACTION_CHANGE
, [ new PasswordAuthenticationRequest() ] ],
243 [ AuthManager
::ACTION_REMOVE
, [ new PasswordAuthenticationRequest() ] ],
247 public function testProviderRevokeAccessForUser() {
248 $req = new PasswordAuthenticationRequest
;
249 $req->action
= AuthManager
::ACTION_REMOVE
;
250 $req->username
= 'foo';
251 $req->password
= null;
253 $provider = $this->getMockForAbstractClass(
254 AbstractPasswordPrimaryAuthenticationProvider
::class
256 $provider->expects( $this->once() )
257 ->method( 'providerChangeAuthenticationData' )
260 $provider->providerRevokeAccessForUser( 'foo' );