MediaWiki REL1_34
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}
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
testForAccountCreation( $user, $creator, array $reqs)
Determine whether an account creation may begin.
getAuthenticationRequests( $action, array $options)
Return the applicable list of AuthenticationRequests.
makeError( $message, SimpleCaptcha $captcha)
verifyCaptcha(SimpleCaptcha $captcha, array $reqs, User $user)
Verify submitted captcha.
postAuthentication( $user, AuthenticationResponse $response)
Post-login callback.
testForAuthentication(array $reqs)
Determine whether an authentication may begin.
A base class that implements some of the boilerplate for a PreAuthenticationProvider.
This serves as the entry point to the authentication system.
This is a value object for authentication requests.
This is a value object to hold authentication response data.
PSR-3 logger instance factory.
Demo CAPTCHA (not for production usage) and base class for real CAPTCHAs.
getError()
Return the error from the last passCaptcha* call.
passCaptchaLimited( $index, $word, User $user)
Checks, if the user reached the amount of false CAPTCHAs and give him some vacation or run self::pass...
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:51