AuthPluginPrimaryAuthenticationProvider   D
last analyzed

Complexity

Total Complexity 89

Size/Duplication

Total Lines 394
Duplicated Lines 20.81 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
dl 82
loc 394
rs 4.8717
c 0
b 0
f 0
wmc 89
lcom 1
cbo 10

21 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 34 7
A makeAuthReq() 0 8 2
A setDomain() 0 15 3
A onUserSaveSettings() 0 4 1
A onUserGroupsChanged() 0 4 1
A onUserLoggedIn() 0 10 2
A onLocalUserCreated() 13 13 3
A getUniqueId() 0 3 1
B getAuthenticationRequests() 0 15 6
C beginPrimaryAuthentication() 5 23 10
B testUserCanAuthenticate() 21 21 5
A testUserCanAuthenticateInternal() 0 7 2
D providerRevokeAccessForUser() 0 28 9
B testUserExists() 21 21 5
A providerAllowsPropertyChange() 0 4 1
C providerAllowsAuthenticationDataChange() 7 47 10
C providerChangeAuthenticationData() 0 24 7
A accountCreationType() 0 4 2
A testForAccountCreation() 0 3 1
D beginPrimaryAccountCreation() 5 28 9
A autoCreatedAccount() 10 10 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AuthPluginPrimaryAuthenticationProvider often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AuthPluginPrimaryAuthenticationProvider, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Primary authentication provider wrapper for AuthPlugin
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup Auth
22
 */
23
24
namespace MediaWiki\Auth;
25
26
use AuthPlugin;
27
use User;
28
29
/**
30
 * Primary authentication provider wrapper for AuthPlugin
31
 * @warning If anything depends on the wrapped AuthPlugin being $wgAuth, it won't work with this!
32
 * @ingroup Auth
33
 * @since 1.27
34
 * @deprecated since 1.27
35
 */
36
class AuthPluginPrimaryAuthenticationProvider
37
	extends AbstractPasswordPrimaryAuthenticationProvider
0 ignored issues
show
Coding Style introduced by
The extends keyword must be on the same line as the class name
Loading history...
38
{
39
	private $auth;
40
	private $hasDomain;
41
	private $requestType = null;
42
43
	/**
44
	 * @param AuthPlugin $auth AuthPlugin to wrap
45
	 * @param string|null $requestType Class name of the
46
	 *  PasswordAuthenticationRequest to use. If $auth->domainList() returns
47
	 *  more than one domain, this must be a PasswordDomainAuthenticationRequest.
48
	 */
49
	public function __construct( AuthPlugin $auth, $requestType = null ) {
50
		parent::__construct();
51
52
		if ( $auth instanceof AuthManagerAuthPlugin ) {
53
			throw new \InvalidArgumentException(
54
				'Trying to wrap AuthManagerAuthPlugin in AuthPluginPrimaryAuthenticationProvider ' .
55
					'makes no sense.'
56
			);
57
		}
58
59
		$need = count( $auth->domainList() ) > 1
60
			? PasswordDomainAuthenticationRequest::class
61
			: PasswordAuthenticationRequest::class;
62
		if ( $requestType === null ) {
63
			$requestType = $need;
64
		} elseif ( $requestType !== $need && !is_subclass_of( $requestType, $need ) ) {
65
			throw new \InvalidArgumentException( "$requestType is not a $need" );
66
		}
67
68
		$this->auth = $auth;
69
		$this->requestType = $requestType;
70
		$this->hasDomain = (
71
			$requestType === PasswordDomainAuthenticationRequest::class ||
72
			is_subclass_of( $requestType, PasswordDomainAuthenticationRequest::class )
73
		);
74
		$this->authoritative = $auth->strict();
75
76
		// Registering hooks from core is unusual, but is needed here to be
77
		// able to call the AuthPlugin methods those hooks replace.
78
		\Hooks::register( 'UserSaveSettings', [ $this, 'onUserSaveSettings' ] );
79
		\Hooks::register( 'UserGroupsChanged', [ $this, 'onUserGroupsChanged' ] );
80
		\Hooks::register( 'UserLoggedIn', [ $this, 'onUserLoggedIn' ] );
81
		\Hooks::register( 'LocalUserCreated', [ $this, 'onLocalUserCreated' ] );
82
	}
83
84
	/**
85
	 * Create an appropriate AuthenticationRequest
86
	 * @return PasswordAuthenticationRequest
87
	 */
88
	protected function makeAuthReq() {
89
		$class = $this->requestType;
90
		if ( $this->hasDomain ) {
91
			return new $class( $this->auth->domainList() );
92
		} else {
93
			return new $class();
94
		}
95
	}
96
97
	/**
98
	 * Call $this->auth->setDomain()
99
	 * @param PasswordAuthenticationRequest $req
100
	 */
101
	protected function setDomain( $req ) {
102
		if ( $this->hasDomain ) {
103
			$domain = $req->domain;
0 ignored issues
show
Bug introduced by
The property domain does not seem to exist in MediaWiki\Auth\PasswordAuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
104
		} else {
105
			// Just grab the first one.
106
			$domainList = $this->auth->domainList();
107
			$domain = reset( $domainList );
108
		}
109
110
		// Special:UserLogin does this. Strange.
111
		if ( !$this->auth->validDomain( $domain ) ) {
112
			$domain = $this->auth->getDomain();
113
		}
114
		$this->auth->setDomain( $domain );
115
	}
116
117
	/**
118
	 * Hook function to call AuthPlugin::updateExternalDB()
119
	 * @param User $user
120
	 * @codeCoverageIgnore
121
	 */
122
	public function onUserSaveSettings( $user ) {
123
		// No way to know the domain, just hope the provider handles that.
124
		$this->auth->updateExternalDB( $user );
125
	}
126
127
	/**
128
	 * Hook function to call AuthPlugin::updateExternalDBGroups()
129
	 * @param User $user
130
	 * @param array $added
131
	 * @param array $removed
132
	 */
133
	public function onUserGroupsChanged( $user, $added, $removed ) {
134
		// No way to know the domain, just hope the provider handles that.
135
		$this->auth->updateExternalDBGroups( $user, $added, $removed );
136
	}
137
138
	/**
139
	 * Hook function to call AuthPlugin::updateUser()
140
	 * @param User $user
141
	 */
142
	public function onUserLoggedIn( $user ) {
143
		$hookUser = $user;
144
		// No way to know the domain, just hope the provider handles that.
145
		$this->auth->updateUser( $hookUser );
146
		if ( $hookUser !== $user ) {
147
			throw new \UnexpectedValueException(
148
				get_class( $this->auth ) . '::updateUser() tried to replace $user!'
149
			);
150
		}
151
	}
152
153
	/**
154
	 * Hook function to call AuthPlugin::initUser()
155
	 * @param User $user
156
	 * @param bool $autocreated
157
	 */
158 View Code Duplication
	public function onLocalUserCreated( $user, $autocreated ) {
159
		// For $autocreated, see self::autoCreatedAccount()
160
		if ( !$autocreated ) {
161
			$hookUser = $user;
162
			// No way to know the domain, just hope the provider handles that.
163
			$this->auth->initUser( $hookUser, $autocreated );
164
			if ( $hookUser !== $user ) {
165
				throw new \UnexpectedValueException(
166
					get_class( $this->auth ) . '::initUser() tried to replace $user!'
167
				);
168
			}
169
		}
170
	}
171
172
	public function getUniqueId() {
173
		return parent::getUniqueId() . ':' . get_class( $this->auth );
174
	}
175
176
	public function getAuthenticationRequests( $action, array $options ) {
177
		switch ( $action ) {
178
			case AuthManager::ACTION_LOGIN:
179
			case AuthManager::ACTION_CREATE:
180
				return [ $this->makeAuthReq() ];
181
182
			case AuthManager::ACTION_CHANGE:
183
			case AuthManager::ACTION_REMOVE:
184
				// No way to know the domain, just hope the provider handles that.
185
				return $this->auth->allowPasswordChange() ? [ $this->makeAuthReq() ] : [];
186
187
			default:
188
				return [];
189
		}
190
	}
191
192
	public function beginPrimaryAuthentication( array $reqs ) {
193
		$req = AuthenticationRequest::getRequestByClass( $reqs, $this->requestType );
194 View Code Duplication
		if ( !$req || $req->username === null || $req->password === null ||
0 ignored issues
show
Bug introduced by
The property password does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
195
			( $this->hasDomain && $req->domain === null )
0 ignored issues
show
Bug introduced by
The property domain does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
196
		) {
197
			return AuthenticationResponse::newAbstain();
198
		}
199
200
		$username = User::getCanonicalName( $req->username, 'usable' );
201
		if ( $username === false ) {
202
			return AuthenticationResponse::newAbstain();
203
		}
204
205
		$this->setDomain( $req );
0 ignored issues
show
Compatibility introduced by
$req of type object<MediaWiki\Auth\AuthenticationRequest> is not a sub-type of object<MediaWiki\Auth\Pa...dAuthenticationRequest>. It seems like you assume a child class of the class MediaWiki\Auth\AuthenticationRequest to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
206
		if ( $this->testUserCanAuthenticateInternal( User::newFromName( $username ) ) &&
0 ignored issues
show
Bug introduced by
It seems like $username defined by \User::getCanonicalName($req->username, 'usable') on line 200 can also be of type boolean; however, User::newFromName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Security Bug introduced by
It seems like \User::newFromName($username) targeting User::newFromName() can also be of type false; however, MediaWiki\Auth\AuthPlugi...nAuthenticateInternal() does only seem to accept object<User>, did you maybe forget to handle an error condition?
Loading history...
207
			$this->auth->authenticate( $username, $req->password )
0 ignored issues
show
Bug introduced by
It seems like $username defined by \User::getCanonicalName($req->username, 'usable') on line 200 can also be of type boolean; however, AuthPlugin::authenticate() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
208
		) {
209
			return AuthenticationResponse::newPass( $username );
0 ignored issues
show
Bug introduced by
It seems like $username defined by \User::getCanonicalName($req->username, 'usable') on line 200 can also be of type boolean; however, MediaWiki\Auth\AuthenticationResponse::newPass() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
210
		} else {
211
			$this->authoritative = $this->auth->strict() || $this->auth->strictUserAuth( $username );
0 ignored issues
show
Bug introduced by
It seems like $username defined by \User::getCanonicalName($req->username, 'usable') on line 200 can also be of type boolean; however, AuthPlugin::strictUserAuth() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
212
			return $this->failResponse( $req );
0 ignored issues
show
Compatibility introduced by
$req of type object<MediaWiki\Auth\AuthenticationRequest> is not a sub-type of object<MediaWiki\Auth\Pa...dAuthenticationRequest>. It seems like you assume a child class of the class MediaWiki\Auth\AuthenticationRequest to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
213
		}
214
	}
215
216 View Code Duplication
	public function testUserCanAuthenticate( $username ) {
217
		$username = User::getCanonicalName( $username, 'usable' );
218
		if ( $username === false ) {
219
			return false;
220
		}
221
222
		// We have to check every domain, because at least LdapAuthentication
223
		// interprets AuthPlugin::userExists() as applying only to the current
224
		// domain.
225
		$curDomain = $this->auth->getDomain();
226
		$domains = $this->auth->domainList() ?: [ '' ];
227
		foreach ( $domains as $domain ) {
228
			$this->auth->setDomain( $domain );
229
			if ( $this->testUserCanAuthenticateInternal( User::newFromName( $username ) ) ) {
0 ignored issues
show
Bug introduced by
It seems like $username defined by \User::getCanonicalName($username, 'usable') on line 217 can also be of type boolean; however, User::newFromName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Security Bug introduced by
It seems like \User::newFromName($username) targeting User::newFromName() can also be of type false; however, MediaWiki\Auth\AuthPlugi...nAuthenticateInternal() does only seem to accept object<User>, did you maybe forget to handle an error condition?
Loading history...
230
				$this->auth->setDomain( $curDomain );
231
				return true;
232
			}
233
		}
234
		$this->auth->setDomain( $curDomain );
235
		return false;
236
	}
237
238
	/**
239
	 * @see self::testUserCanAuthenticate
240
	 * @note The caller is responsible for calling $this->auth->setDomain()
241
	 * @param User $user
242
	 * @return bool
243
	 */
244
	private function testUserCanAuthenticateInternal( $user ) {
245
		if ( $this->auth->userExists( $user->getName() ) ) {
246
			return !$this->auth->getUserInstance( $user )->isLocked();
247
		} else {
248
			return false;
249
		}
250
	}
251
252
	public function providerRevokeAccessForUser( $username ) {
253
		$username = User::getCanonicalName( $username, 'usable' );
254
		if ( $username === false ) {
255
			return;
256
		}
257
		$user = User::newFromName( $username );
0 ignored issues
show
Bug introduced by
It seems like $username defined by \User::getCanonicalName($username, 'usable') on line 253 can also be of type boolean; however, User::newFromName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
258
		if ( $user ) {
259
			// Reset the password on every domain.
260
			$curDomain = $this->auth->getDomain();
261
			$domains = $this->auth->domainList() ?: [ '' ];
262
			$failed = [];
263
			foreach ( $domains as $domain ) {
264
				$this->auth->setDomain( $domain );
265
				if ( $this->testUserCanAuthenticateInternal( $user ) &&
266
					!$this->auth->setPassword( $user, null )
267
				) {
268
					$failed[] = $domain === '' ? '(default)' : $domain;
269
				}
270
			}
271
			$this->auth->setDomain( $curDomain );
272
			if ( $failed ) {
273
				throw new \UnexpectedValueException(
274
					"AuthPlugin failed to reset password for $username in the following domains: "
275
						. join( ' ', $failed )
276
				);
277
			}
278
		}
279
	}
280
281 View Code Duplication
	public function testUserExists( $username, $flags = User::READ_NORMAL ) {
282
		$username = User::getCanonicalName( $username, 'usable' );
283
		if ( $username === false ) {
284
			return false;
285
		}
286
287
		// We have to check every domain, because at least LdapAuthentication
288
		// interprets AuthPlugin::userExists() as applying only to the current
289
		// domain.
290
		$curDomain = $this->auth->getDomain();
291
		$domains = $this->auth->domainList() ?: [ '' ];
292
		foreach ( $domains as $domain ) {
293
			$this->auth->setDomain( $domain );
294
			if ( $this->auth->userExists( $username ) ) {
0 ignored issues
show
Bug introduced by
It seems like $username defined by \User::getCanonicalName($username, 'usable') on line 282 can also be of type boolean; however, AuthPlugin::userExists() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
295
				$this->auth->setDomain( $curDomain );
296
				return true;
297
			}
298
		}
299
		$this->auth->setDomain( $curDomain );
300
		return false;
301
	}
302
303
	public function providerAllowsPropertyChange( $property ) {
304
		// No way to know the domain, just hope the provider handles that.
305
		return $this->auth->allowPropChange( $property );
306
	}
307
308
	public function providerAllowsAuthenticationDataChange(
309
		AuthenticationRequest $req, $checkData = true
310
	) {
311
		if ( get_class( $req ) !== $this->requestType ) {
312
			return \StatusValue::newGood( 'ignored' );
313
		}
314
315
		// Hope it works, AuthPlugin gives us no way to do this.
316
		$curDomain = $this->auth->getDomain();
317
		$this->setDomain( $req );
0 ignored issues
show
Compatibility introduced by
$req of type object<MediaWiki\Auth\AuthenticationRequest> is not a sub-type of object<MediaWiki\Auth\Pa...dAuthenticationRequest>. It seems like you assume a child class of the class MediaWiki\Auth\AuthenticationRequest to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
318
		try {
319
			// If !$checkData the domain might be wrong. Nothing we can do about that.
320
			if ( !$this->auth->allowPasswordChange() ) {
321
				return \StatusValue::newFatal( 'authmanager-authplugin-setpass-denied' );
322
			}
323
324
			if ( !$checkData ) {
325
				return \StatusValue::newGood();
326
			}
327
328
			if ( $this->hasDomain ) {
329
				if ( $req->domain === null ) {
0 ignored issues
show
Bug introduced by
The property domain does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
330
					return \StatusValue::newGood( 'ignored' );
331
				}
332
				if ( !$this->auth->validDomain( $req->domain ) ) {
333
					return \StatusValue::newFatal( 'authmanager-authplugin-setpass-bad-domain' );
334
				}
335
			}
336
337
			$username = User::getCanonicalName( $req->username, 'usable' );
338
			if ( $username !== false ) {
339
				$sv = \StatusValue::newGood();
340 View Code Duplication
				if ( $req->password !== null ) {
341
					if ( $req->password !== $req->retype ) {
0 ignored issues
show
Bug introduced by
The property retype does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
342
						$sv->fatal( 'badretype' );
343
					} else {
344
						$sv->merge( $this->checkPasswordValidity( $username, $req->password ) );
0 ignored issues
show
Bug introduced by
The property password does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Bug introduced by
It seems like $username defined by \User::getCanonicalName($req->username, 'usable') on line 337 can also be of type boolean; however, MediaWiki\Auth\AbstractP...checkPasswordValidity() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
345
					}
346
				}
347
				return $sv;
348
			} else {
349
				return \StatusValue::newGood( 'ignored' );
350
			}
351
		} finally {
352
			$this->auth->setDomain( $curDomain );
353
		}
354
	}
355
356
	public function providerChangeAuthenticationData( AuthenticationRequest $req ) {
357
		if ( get_class( $req ) === $this->requestType ) {
358
			$username = $req->username !== null ? User::getCanonicalName( $req->username, 'usable' ) : false;
359
			if ( $username === false ) {
360
				return;
361
			}
362
363
			if ( $this->hasDomain && $req->domain === null ) {
0 ignored issues
show
Bug introduced by
The property domain does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
364
				return;
365
			}
366
367
			$this->setDomain( $req );
0 ignored issues
show
Compatibility introduced by
$req of type object<MediaWiki\Auth\AuthenticationRequest> is not a sub-type of object<MediaWiki\Auth\Pa...dAuthenticationRequest>. It seems like you assume a child class of the class MediaWiki\Auth\AuthenticationRequest to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
368
			$user = User::newFromName( $username );
0 ignored issues
show
Bug introduced by
It seems like $username defined by $req->username !== null ...name, 'usable') : false on line 358 can also be of type boolean; however, User::newFromName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
369
			if ( !$this->auth->setPassword( $user, $req->password ) ) {
0 ignored issues
show
Bug introduced by
The property password does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
Security Bug introduced by
It seems like $user defined by \User::newFromName($username) on line 368 can also be of type false; however, AuthPlugin::setPassword() does only seem to accept object<User>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
370
				// This is totally unfriendly and leaves other
371
				// AuthenticationProviders in an uncertain state, but what else
372
				// can we do?
373
				throw new \ErrorPageError(
374
					'authmanager-authplugin-setpass-failed-title',
375
					'authmanager-authplugin-setpass-failed-message'
376
				);
377
			}
378
		}
379
	}
380
381
	public function accountCreationType() {
382
		// No way to know the domain, just hope the provider handles that.
383
		return $this->auth->canCreateAccounts() ? self::TYPE_CREATE : self::TYPE_NONE;
384
	}
385
386
	public function testForAccountCreation( $user, $creator, array $reqs ) {
387
		return \StatusValue::newGood();
388
	}
389
390
	public function beginPrimaryAccountCreation( $user, $creator, array $reqs ) {
391
		if ( $this->accountCreationType() === self::TYPE_NONE ) {
392
			throw new \BadMethodCallException( 'Shouldn\'t call this when accountCreationType() is NONE' );
393
		}
394
395
		$req = AuthenticationRequest::getRequestByClass( $reqs, $this->requestType );
396 View Code Duplication
		if ( !$req || $req->username === null || $req->password === null ||
0 ignored issues
show
Bug introduced by
The property password does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
397
			( $this->hasDomain && $req->domain === null )
0 ignored issues
show
Bug introduced by
The property domain does not seem to exist in MediaWiki\Auth\AuthenticationRequest.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
398
		) {
399
			return AuthenticationResponse::newAbstain();
400
		}
401
402
		$username = User::getCanonicalName( $req->username, 'usable' );
403
		if ( $username === false ) {
404
			return AuthenticationResponse::newAbstain();
405
		}
406
407
		$this->setDomain( $req );
0 ignored issues
show
Compatibility introduced by
$req of type object<MediaWiki\Auth\AuthenticationRequest> is not a sub-type of object<MediaWiki\Auth\Pa...dAuthenticationRequest>. It seems like you assume a child class of the class MediaWiki\Auth\AuthenticationRequest to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
408
		if ( $this->auth->addUser(
409
			$user, $req->password, $user->getEmail(), $user->getRealName()
410
		) ) {
411
			return AuthenticationResponse::newPass();
412
		} else {
413
			return AuthenticationResponse::newFail(
414
				new \Message( 'authmanager-authplugin-create-fail' )
415
			);
416
		}
417
	}
418
419 View Code Duplication
	public function autoCreatedAccount( $user, $source ) {
420
		$hookUser = $user;
421
		// No way to know the domain, just hope the provider handles that.
422
		$this->auth->initUser( $hookUser, true );
423
		if ( $hookUser !== $user ) {
424
			throw new \UnexpectedValueException(
425
				get_class( $this->auth ) . '::initUser() tried to replace $user!'
426
			);
427
		}
428
	}
429
}
430