MediaWiki  1.34.0
CaptchaPreAuthenticationProvider.php
Go to the documentation of this file.
1 <?php
2 
8 
10  public function getAuthenticationRequests( $action, array $options ) {
11  $captcha = ConfirmEditHooks::getInstance();
12  $user = User::newFromName( $options['username'] );
13 
14  $needed = false;
15  switch ( $action ) {
16  case AuthManager::ACTION_CREATE:
17  $needed = $captcha->needCreateAccountCaptcha( $user ?: new User() );
18  if ( $needed ) {
19  $captcha->setAction( 'accountcreate' );
20  LoggerFactory::getInstance( 'authevents' )
21  ->info( 'Captcha shown on account creation', [
22  'event' => 'captcha.display',
23  'eventType' => 'accountcreation',
24  ] );
25  }
26  break;
27  case AuthManager::ACTION_LOGIN:
28  // Captcha is shown on login when there were too many failed attempts from the
29  // current IP or user. The latter is a bit awkward because we don't know the
30  // username yet. The username from the last successful login is stored in a cookie,
31  // but we still must make sure to not lock out other usernames so we use a session
32  // flag. This will result in confusing error messages if the browser cannot persist
33  // the session, but then login would be impossible anyway so no big deal.
34 
35  // If the username ends to be one that does not trigger the captcha, that will
36  // result in weird behavior (if the user leaves the captcha field open, they get
37  // a required field error, if they fill it with an invalid answer, it will pass)
38  // - again, not a huge deal.
39  $session = $this->manager->getRequest()->getSession();
40  $sessionFlag = $session->get( 'ConfirmEdit:loginCaptchaPerUserTriggered' );
41  $suggestedUsername = $session->suggestLoginUsername();
42  if (
43  $captcha->isBadLoginTriggered()
44  || $sessionFlag
45  || $suggestedUsername && $captcha->isBadLoginPerUserTriggered( $suggestedUsername )
46  ) {
47  $needed = true;
48  $captcha->setAction( 'badlogin' );
49  LoggerFactory::getInstance( 'authevents' )
50  ->info( 'Captcha shown on account creation', [
51  'event' => 'captcha.display',
52  'eventType' => 'accountcreation',
53  ] );
54  break;
55  }
56  break;
57  }
58 
59  if ( $needed ) {
60  return [ $captcha->createAuthenticationRequest() ];
61  } else {
62  return [];
63  }
64  }
65 
66  public function testForAuthentication( array $reqs ) {
67  $captcha = ConfirmEditHooks::getInstance();
68  $username = AuthenticationRequest::getUsernameFromRequests( $reqs );
69  $success = true;
70  $isBadLoginPerUserTriggered = $username ?
71  $captcha->isBadLoginPerUserTriggered( $username ) : false;
72 
73  if ( $captcha->isBadLoginTriggered() || $isBadLoginPerUserTriggered ) {
74  $captcha->setAction( 'badlogin' );
75  $captcha->setTrigger( "post-badlogin login '$username'" );
76  $success = $this->verifyCaptcha( $captcha, $reqs, new User() );
77  LoggerFactory::getInstance( 'authevents' )->info( 'Captcha submitted on login', [
78  'event' => 'captcha.submit',
79  'eventType' => 'login',
80  'successful' => $success,
81  ] );
82  }
83 
84  if ( $isBadLoginPerUserTriggered || $isBadLoginPerUserTriggered === null ) {
85  $session = $this->manager->getRequest()->getSession();
86  $session->set( 'ConfirmEdit:loginCaptchaPerUserTriggered', true );
87  }
88 
89  // Make brute force attacks harder by not telling whether the password or the
90  // captcha failed.
91  return $success ? Status::newGood() : $this->makeError( 'wrongpassword', $captcha );
92  }
93 
94  public function testForAccountCreation( $user, $creator, array $reqs ) {
95  $captcha = ConfirmEditHooks::getInstance();
96 
97  if ( $captcha->needCreateAccountCaptcha( $creator ) ) {
98  $username = $user->getName();
99  $captcha->setAction( 'accountcreate' );
100  $captcha->setTrigger( "new account '$username'" );
101  $success = $this->verifyCaptcha( $captcha, $reqs, $user );
102  LoggerFactory::getInstance( 'authevents' )->info( 'Captcha submitted on account creation', [
103  'event' => 'captcha.submit',
104  'eventType' => 'accountcreation',
105  'successful' => $success,
106  ] );
107  if ( !$success ) {
108  return $this->makeError( 'captcha-createaccount-fail', $captcha );
109  }
110  }
111  return Status::newGood();
112  }
113 
115  $captcha = ConfirmEditHooks::getInstance();
116  switch ( $response->status ) {
117  case AuthenticationResponse::PASS:
118  case AuthenticationResponse::RESTART:
119  $session = $this->manager->getRequest()->getSession();
120  $session->remove( 'ConfirmEdit:loginCaptchaPerUserTriggered' );
121  $captcha->resetBadLoginCounter( $user ? $user->getName() : null );
122  break;
123  case AuthenticationResponse::FAIL:
124  $captcha->increaseBadLoginCounter( $user ? $user->getName() : null );
125  break;
126  }
127  }
128 
137  protected function verifyCaptcha( SimpleCaptcha $captcha, array $reqs, User $user ) {
139  $req = AuthenticationRequest::getRequestByClass( $reqs,
140  CaptchaAuthenticationRequest::class, true );
141  if ( !$req ) {
142  return false;
143  }
144  return $captcha->passCaptchaLimited( $req->captchaId, $req->captchaWord, $user );
145  }
146 
152  protected function makeError( $message, SimpleCaptcha $captcha ) {
153  $error = $captcha->getError();
154  if ( $error ) {
155  return Status::newFatal( wfMessage( 'captcha-error', $error ) );
156  }
157  return Status::newFatal( $message );
158  }
159 }
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
$response
$response
Definition: opensearch_desc.php:38
CaptchaPreAuthenticationProvider\makeError
makeError( $message, SimpleCaptcha $captcha)
Definition: CaptchaPreAuthenticationProvider.php:152
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:515
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1264
CaptchaPreAuthenticationProvider\postAuthentication
postAuthentication( $user, AuthenticationResponse $response)
Post-login callback.
Definition: CaptchaPreAuthenticationProvider.php:114
ConfirmEditHooks\getInstance
static getInstance()
Get the global Captcha instance.
Definition: ConfirmEditHooks.php:13
$success
$success
Definition: NoLocalSettings.php:42
SimpleCaptcha\passCaptchaLimited
passCaptchaLimited( $index, $word, User $user)
Checks, if the user reached the amount of false CAPTCHAs and give him some vacation or run self::pass...
Definition: SimpleCaptcha.php:973
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
CaptchaPreAuthenticationProvider\getAuthenticationRequests
getAuthenticationRequests( $action, array $options)
Return the applicable list of AuthenticationRequests.
Definition: CaptchaPreAuthenticationProvider.php:10
CaptchaPreAuthenticationProvider\testForAccountCreation
testForAccountCreation( $user, $creator, array $reqs)
Determine whether an account creation may begin.
Definition: CaptchaPreAuthenticationProvider.php:94
MediaWiki\Auth\AuthenticationResponse
This is a value object to hold authentication response data.
Definition: AuthenticationResponse.php:37
CaptchaPreAuthenticationProvider
Definition: CaptchaPreAuthenticationProvider.php:9
CaptchaPreAuthenticationProvider\testForAuthentication
testForAuthentication(array $reqs)
Determine whether an authentication may begin.
Definition: CaptchaPreAuthenticationProvider.php:66
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
SimpleCaptcha
Demo CAPTCHA (not for production usage) and base class for real CAPTCHAs.
Definition: SimpleCaptcha.php:9
SimpleCaptcha\getError
getError()
Return the error from the last passCaptcha* call.
Definition: SimpleCaptcha.php:44
MediaWiki\Auth\AuthManager
This serves as the entry point to the authentication system.
Definition: AuthManager.php:85
CaptchaPreAuthenticationProvider\verifyCaptcha
verifyCaptcha(SimpleCaptcha $captcha, array $reqs, User $user)
Verify submitted captcha.
Definition: CaptchaPreAuthenticationProvider.php:137
MediaWiki\Auth\AbstractPreAuthenticationProvider
A base class that implements some of the boilerplate for a PreAuthenticationProvider.
Definition: AbstractPreAuthenticationProvider.php:29
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:51
MediaWiki\Auth\AuthenticationRequest
This is a value object for authentication requests.
Definition: AuthenticationRequest.php:37