MediaWiki  REL1_31
AuthPluginPrimaryAuthenticationProvider.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Auth;
25 
27 use User;
28 
38 {
39  private $auth;
40  private $hasDomain;
41  private $requestType = null;
42 
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
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 = (
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 
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 
101  protected function setDomain( $req ) {
102  if ( $this->hasDomain ) {
103  $domain = $req->domain;
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 
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 
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 
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 
158  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 
177  switch ( $action ) {
180  return [ $this->makeAuthReq() ];
181 
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  if ( !$req || $req->username === null || $req->password === null ||
195  ( $this->hasDomain && $req->domain === null )
196  ) {
198  }
199 
200  $username = User::getCanonicalName( $req->username, 'usable' );
201  if ( $username === false ) {
203  }
204 
205  $this->setDomain( $req );
207  $this->auth->authenticate( $username, $req->password )
208  ) {
210  } else {
211  $this->authoritative = $this->auth->strict() || $this->auth->strictUserAuth( $username );
212  return $this->failResponse( $req );
213  }
214  }
215 
216  public function testUserCanAuthenticate( $username ) {
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 );
230  $this->auth->setDomain( $curDomain );
231  return true;
232  }
233  }
234  $this->auth->setDomain( $curDomain );
235  return false;
236  }
237 
245  if ( $this->auth->userExists( $user->getName() ) ) {
246  return !$this->auth->getUserInstance( $user )->isLocked();
247  } else {
248  return false;
249  }
250  }
251 
254  if ( $username === false ) {
255  return;
256  }
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  . implode( ' ', $failed )
276  );
277  }
278  }
279  }
280 
281  public function testUserExists( $username, $flags = User::READ_NORMAL ) {
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 ) ) {
295  $this->auth->setDomain( $curDomain );
296  return true;
297  }
298  }
299  $this->auth->setDomain( $curDomain );
300  return false;
301  }
302 
304  // No way to know the domain, just hope the provider handles that.
305  return $this->auth->allowPropChange( $property );
306  }
307 
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 );
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 ) {
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  if ( $req->password !== null ) {
341  if ( $req->password !== $req->retype ) {
342  $sv->fatal( 'badretype' );
343  } else {
344  $sv->merge( $this->checkPasswordValidity( $username, $req->password ) );
345  }
346  }
347  return $sv;
348  } else {
349  return \StatusValue::newGood( 'ignored' );
350  }
351  } finally {
352  $this->auth->setDomain( $curDomain );
353  }
354  }
355 
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 ) {
364  return;
365  }
366 
367  $this->setDomain( $req );
369  if ( !$this->auth->setPassword( $user, $req->password ) ) {
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  if ( !$req || $req->username === null || $req->password === null ||
397  ( $this->hasDomain && $req->domain === null )
398  ) {
400  }
401 
402  $username = User::getCanonicalName( $req->username, 'usable' );
403  if ( $username === false ) {
405  }
406 
407  $this->setDomain( $req );
408  if ( $this->auth->addUser(
409  $user, $req->password, $user->getEmail(), $user->getRealName()
410  ) ) {
412  } else {
414  new \Message( 'authmanager-authplugin-create-fail' )
415  );
416  }
417  }
418 
419  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 }
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\providerChangeAuthenticationData
providerChangeAuthenticationData(AuthenticationRequest $req)
Change or remove authentication data (e.g.
Definition: AuthPluginPrimaryAuthenticationProvider.php:356
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:247
MediaWiki\Auth\PrimaryAuthenticationProvider\TYPE_CREATE
const TYPE_CREATE
Provider can create accounts.
Definition: PrimaryAuthenticationProvider.php:77
MediaWiki\$action
String $action
Cache what action this request is.
Definition: MediaWiki.php:48
MediaWiki\Auth\PrimaryAuthenticationProvider\TYPE_NONE
const TYPE_NONE
Provider cannot create or link to accounts.
Definition: PrimaryAuthenticationProvider.php:81
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\__construct
__construct(AuthPlugin $auth, $requestType=null)
Definition: AuthPluginPrimaryAuthenticationProvider.php:49
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\providerAllowsAuthenticationDataChange
providerAllowsAuthenticationDataChange(AuthenticationRequest $req, $checkData=true)
Validate a change of authentication data (e.g.
Definition: AuthPluginPrimaryAuthenticationProvider.php:308
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\testUserExists
testUserExists( $username, $flags=User::READ_NORMAL)
Test whether the named user exists.
Definition: AuthPluginPrimaryAuthenticationProvider.php:281
use
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Definition: APACHE-LICENSE-2.0.txt:10
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\testUserCanAuthenticate
testUserCanAuthenticate( $username)
Test whether the named user can authenticate with this provider.
Definition: AuthPluginPrimaryAuthenticationProvider.php:216
array
the array() calling protocol came about after MediaWiki 1.4rc1.
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\getUniqueId
getUniqueId()
@inheritDoc
Definition: AuthPluginPrimaryAuthenticationProvider.php:172
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\onUserGroupsChanged
onUserGroupsChanged( $user, $added, $removed)
Hook function to call AuthPlugin::updateExternalDBGroups()
Definition: AuthPluginPrimaryAuthenticationProvider.php:133
MediaWiki\Auth\AbstractPasswordPrimaryAuthenticationProvider\failResponse
failResponse(PasswordAuthenticationRequest $req)
Return the appropriate response for failure.
Definition: AbstractPasswordPrimaryAuthenticationProvider.php:83
MediaWiki\Auth\AuthManagerAuthPlugin
Backwards-compatibility wrapper for AuthManager via $wgAuth.
Definition: AuthManagerAuthPlugin.php:31
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\beginPrimaryAccountCreation
beginPrimaryAccountCreation( $user, $creator, array $reqs)
Start an account creation flow.
Definition: AuthPluginPrimaryAuthenticationProvider.php:390
MediaWiki\Auth\AbstractPasswordPrimaryAuthenticationProvider
Basic framework for a primary authentication provider that uses passwords.
Definition: AbstractPasswordPrimaryAuthenticationProvider.php:35
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\$hasDomain
$hasDomain
Definition: AuthPluginPrimaryAuthenticationProvider.php:40
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\onUserSaveSettings
onUserSaveSettings( $user)
Hook function to call AuthPlugin::updateExternalDB()
Definition: AuthPluginPrimaryAuthenticationProvider.php:122
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\testUserCanAuthenticateInternal
testUserCanAuthenticateInternal( $user)
Definition: AuthPluginPrimaryAuthenticationProvider.php:244
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:591
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\beginPrimaryAuthentication
beginPrimaryAuthentication(array $reqs)
Start an authentication flow.
Definition: AuthPluginPrimaryAuthenticationProvider.php:192
User
User
Definition: All_system_messages.txt:425
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\setDomain
setDomain( $req)
Call $this->auth->setDomain()
Definition: AuthPluginPrimaryAuthenticationProvider.php:101
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider
Primary authentication provider wrapper for AuthPlugin.
Definition: AuthPluginPrimaryAuthenticationProvider.php:38
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\autoCreatedAccount
autoCreatedAccount( $user, $source)
Post-auto-creation callback.
Definition: AuthPluginPrimaryAuthenticationProvider.php:419
MediaWiki\Auth\AuthenticationRequest\getRequestByClass
static getRequestByClass(array $reqs, $class, $allowSubclasses=false)
Select a request by class name.
Definition: AuthenticationRequest.php:253
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:37
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\getAuthenticationRequests
getAuthenticationRequests( $action, array $options)
@inheritDoc
Definition: AuthPluginPrimaryAuthenticationProvider.php:176
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\$auth
$auth
Definition: AuthPluginPrimaryAuthenticationProvider.php:39
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\onLocalUserCreated
onLocalUserCreated( $user, $autocreated)
Hook function to call AuthPlugin::initUser()
Definition: AuthPluginPrimaryAuthenticationProvider.php:158
MediaWiki\Auth\AuthenticationResponse\newAbstain
static newAbstain()
Definition: AuthenticationResponse.php:170
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\testForAccountCreation
testForAccountCreation( $user, $creator, array $reqs)
Determine whether an account creation may begin.
Definition: AuthPluginPrimaryAuthenticationProvider.php:386
$property
$property
Definition: styleTest.css.php:44
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\makeAuthReq
makeAuthReq()
Create an appropriate AuthenticationRequest.
Definition: AuthPluginPrimaryAuthenticationProvider.php:88
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\providerAllowsPropertyChange
providerAllowsPropertyChange( $property)
Determine whether a property can change.
Definition: AuthPluginPrimaryAuthenticationProvider.php:303
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:2001
MediaWiki\Auth\AuthManager\ACTION_CREATE
const ACTION_CREATE
Create a new user.
Definition: AuthManager.php:90
Hooks\register
static register( $name, $callback)
Attach an event handler to a given hook.
Definition: Hooks.php:49
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\onUserLoggedIn
onUserLoggedIn( $user)
Hook function to call AuthPlugin::updateUser()
Definition: AuthPluginPrimaryAuthenticationProvider.php:142
MediaWiki\Auth\AuthManager\ACTION_CHANGE
const ACTION_CHANGE
Change a user's credentials.
Definition: AuthManager.php:100
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\accountCreationType
accountCreationType()
Fetch the account-creation type.
Definition: AuthPluginPrimaryAuthenticationProvider.php:381
MediaWiki\Auth\AbstractPasswordPrimaryAuthenticationProvider\checkPasswordValidity
checkPasswordValidity( $username, $password)
Check that the password is valid.
Definition: AbstractPasswordPrimaryAuthenticationProvider.php:103
$req
this hook is for auditing only $req
Definition: hooks.txt:990
MediaWiki\Auth\AuthManager\ACTION_REMOVE
const ACTION_REMOVE
Remove a user's credentials.
Definition: AuthManager.php:102
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\providerRevokeAccessForUser
providerRevokeAccessForUser( $username)
@inheritDoc
Definition: AuthPluginPrimaryAuthenticationProvider.php:252
MediaWiki\Auth\AuthenticationResponse\newFail
static newFail(Message $msg)
Definition: AuthenticationResponse.php:146
User\getCanonicalName
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
Definition: User.php:1210
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:22
Message
The Message class provides methods which fulfil two basic services:
Definition: Message.php:159
$source
$source
Definition: mwdoc-filter.php:46
MediaWiki\Auth\AuthManager\ACTION_LOGIN
const ACTION_LOGIN
Log in with an existing (not necessarily local) user.
Definition: AuthManager.php:85
$username
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:785
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:56
MediaWiki\Auth\AuthPluginPrimaryAuthenticationProvider\$requestType
$requestType
Definition: AuthPluginPrimaryAuthenticationProvider.php:41
MediaWiki\Auth
Definition: AbstractAuthenticationProvider.php:22
MediaWiki\Auth\AuthenticationResponse\newPass
static newPass( $username=null)
Definition: AuthenticationResponse.php:134
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:37
AuthPlugin
Authentication plugin interface.
Definition: AuthPlugin.php:38