1<?php
2
3namespace MediaWiki\Auth;
4
5use MediaWiki\MediaWikiServices;
6use MediaWiki\Tests\Unit\Auth\AuthenticationProviderTestTrait;
7use MediaWiki\User\UserNameUtils;
8use Psr\Container\ContainerInterface;
9use Psr\Log\LoggerInterface;
10use Wikimedia\TestingAccessWrapper;
11
12/**
13 * @covers \MediaWiki\Auth\EmailNotificationSecondaryAuthenticationProvider
14 * @group Database
15 */
16class EmailNotificationSecondaryAuthenticationProviderTest extends \MediaWikiIntegrationTestCase {
17	use AuthenticationProviderTestTrait;
18
19	/**
20	 * @param array $options
21	 * @return EmailNotificationSecondaryAuthenticationProvider
22	 */
23	private function getProvider( array $options = [] ): EmailNotificationSecondaryAuthenticationProvider {
24		$services = $this->getServiceContainer();
25		$provider = new EmailNotificationSecondaryAuthenticationProvider(
26			$options['loadBalancer'] ?? $services->getDBLoadBalancer(),
27			$options // make things easier for tests by using the same options
28		);
29		$this->initProvider(
30			$provider,
31			$options['config'] ?? null,
32			$options['logger'] ?? null,
33			$options['authManager'] ?? null,
34			$options['hookContainer'] ?? null,
35			$options['userNameUtils'] ?? null
36		);
37		return $provider;
38	}
39
40	public function testConstructor() {
41		$config = new \HashConfig( [
42			'EnableEmail' => true,
43			'EmailAuthentication' => true,
44		] );
45
46		$provider = $this->getProvider( [
47			'config' => $config,
48		] );
49		$providerPriv = TestingAccessWrapper::newFromObject( $provider );
50		$this->assertTrue( $providerPriv->sendConfirmationEmail );
51
52		$provider = $this->getProvider( [
53			'config' => $config,
54			'sendConfirmationEmail' => false,
55		] );
56		$providerPriv = TestingAccessWrapper::newFromObject( $provider );
57		$this->assertFalse( $providerPriv->sendConfirmationEmail );
58	}
59
60	/**
61	 * @dataProvider provideGetAuthenticationRequests
62	 * @param string $action
63	 * @param AuthenticationRequest[] $expected
64	 */
65	public function testGetAuthenticationRequests( $action, $expected ) {
66		$provider = $this->getProvider( [
67			'sendConfirmationEmail' => true,
68		] );
69		$this->assertSame( $expected, $provider->getAuthenticationRequests( $action, [] ) );
70	}
71
72	public function provideGetAuthenticationRequests() {
73		return [
74			[ AuthManager::ACTION_LOGIN, [] ],
75			[ AuthManager::ACTION_CREATE, [] ],
76			[ AuthManager::ACTION_LINK, [] ],
77			[ AuthManager::ACTION_CHANGE, [] ],
78			[ AuthManager::ACTION_REMOVE, [] ],
79		];
80	}
81
82	public function testBeginSecondaryAuthentication() {
83		$provider = $this->getProvider( [
84			'sendConfirmationEmail' => true,
85		] );
86		$this->assertEquals( AuthenticationResponse::newAbstain(),
87			$provider->beginSecondaryAuthentication( \User::newFromName( 'Foo' ), [] ) );
88	}
89
90	public function testBeginSecondaryAccountCreation() {
91		$mwServices = MediaWikiServices::getInstance();
92		$services = $this->createNoOpAbstractMock( ContainerInterface::class );
93		$objectFactory = new \Wikimedia\ObjectFactory( $services );
94		$hookContainer = $this->createHookContainer();
95		$userNameUtils = $this->createNoOpMock( UserNameUtils::class );
96		$authManager = new AuthManager(
97			new \FauxRequest(),
98			new \HashConfig(),
99			$objectFactory,
100			$hookContainer,
101			$mwServices->getReadOnlyMode(),
102			$userNameUtils,
103			$mwServices->getBlockManager(),
104			$mwServices->getWatchlistManager(),
105			$mwServices->getDBLoadBalancer(),
106			$mwServices->getContentLanguage(),
107			$mwServices->getLanguageConverterFactory(),
108			$mwServices->getBotPasswordStore(),
109			$mwServices->getUserFactory(),
110			$mwServices->getUserIdentityLookup(),
111			$mwServices->getUserOptionsManager()
112		);
113
114		$creator = $this->getMockBuilder( \User::class )->getMock();
115		$userWithoutEmail = $this->getMockBuilder( \User::class )->getMock();
116		$userWithoutEmail->method( 'getEmail' )->willReturn( '' );
117		$userWithoutEmail->method( 'getInstanceForUpdate' )->willReturnSelf();
118		$userWithoutEmail->expects( $this->never() )->method( 'sendConfirmationMail' );
119		$userWithEmailError = $this->getMockBuilder( \User::class )->getMock();
120		$userWithEmailError->method( 'getEmail' )->willReturn( 'foo@bar.baz' );
121		$userWithEmailError->method( 'getInstanceForUpdate' )->willReturnSelf();
122		$userWithEmailError->method( 'sendConfirmationMail' )
123			->willReturn( \Status::newFatal( 'fail' ) );
124		$userExpectsConfirmation = $this->getMockBuilder( \User::class )->getMock();
125		$userExpectsConfirmation->method( 'getEmail' )
126			->willReturn( 'foo@bar.baz' );
127		$userExpectsConfirmation->method( 'getInstanceForUpdate' )
128			->willReturnSelf();
129		$userExpectsConfirmation->expects( $this->once() )->method( 'sendConfirmationMail' )
130			->willReturn( \Status::newGood() );
131		$userNotExpectsConfirmation = $this->getMockBuilder( \User::class )->getMock();
132		$userNotExpectsConfirmation->method( 'getEmail' )
133			->willReturn( 'foo@bar.baz' );
134		$userNotExpectsConfirmation->method( 'getInstanceForUpdate' )
135			->willReturnSelf();
136		$userNotExpectsConfirmation->expects( $this->never() )->method( 'sendConfirmationMail' );
137
138		$provider = $this->getProvider( [
139			'sendConfirmationEmail' => false,
140			'authManager' => $authManager,
141			'hookContainer' => $hookContainer,
142			'userNameUtils' => $userNameUtils
143		] );
144		$provider->beginSecondaryAccountCreation( $userNotExpectsConfirmation, $creator, [] );
145
146		$provider = $this->getProvider( [
147			'sendConfirmationEmail' => true,
148			'authManager' => $authManager,
149			'userNameUtils' => $userNameUtils
150		] );
151		$provider->beginSecondaryAccountCreation( $userWithoutEmail, $creator, [] );
152		$provider->beginSecondaryAccountCreation( $userExpectsConfirmation, $creator, [] );
153
154		// test logging of email errors
155		$logger = $this->getMockForAbstractClass( LoggerInterface::class );
156		$logger->expects( $this->once() )->method( 'warning' );
157		$this->initProvider( $provider, null, $logger, $authManager );
158		$provider->beginSecondaryAccountCreation( $userWithEmailError, $creator, [] );
159
160		// test disable flag used by other providers
161		$authManager->setAuthenticationSessionData( 'no-email', true );
162		$this->initProvider( $provider, null, null, $authManager );
163		$provider->beginSecondaryAccountCreation( $userNotExpectsConfirmation, $creator, [] );
164	}
165}
166