MediaWiki  master
ThrottlePreAuthenticationProvider.php
Go to the documentation of this file.
1 <?php
22 namespace MediaWiki\Auth;
23 
24 use BagOStuff;
25 
38  protected $throttleSettings;
39 
42 
45 
47  protected $cache;
48 
57  public function __construct( $params = [] ) {
58  $this->throttleSettings = array_intersect_key( $params,
59  [ 'accountCreationThrottle' => true, 'passwordAttemptThrottle' => true ] );
60  $this->cache = $params['cache'] ?? \ObjectCache::getLocalClusterInstance();
61  }
62 
63  protected function postInitSetup() {
64  $accountCreationThrottle = $this->config->get( 'AccountCreationThrottle' );
65  // Handle old $wgAccountCreationThrottle format (number of attempts per 24 hours)
66  if ( !is_array( $accountCreationThrottle ) ) {
68  'count' => $accountCreationThrottle,
69  'seconds' => 86400,
70  ] ];
71  }
72 
73  // @codeCoverageIgnoreStart
74  $this->throttleSettings += [
75  // @codeCoverageIgnoreEnd
76  'accountCreationThrottle' => $accountCreationThrottle,
77  'passwordAttemptThrottle' => $this->config->get( 'PasswordAttemptThrottle' ),
78  ];
79 
80  if ( !empty( $this->throttleSettings['accountCreationThrottle'] ) ) {
81  $this->accountCreationThrottle = new Throttler(
82  $this->throttleSettings['accountCreationThrottle'], [
83  'type' => 'acctcreate',
84  'cache' => $this->cache,
85  ]
86  );
87  }
88  if ( !empty( $this->throttleSettings['passwordAttemptThrottle'] ) ) {
89  $this->passwordAttemptThrottle = new Throttler(
90  $this->throttleSettings['passwordAttemptThrottle'], [
91  'type' => 'password',
92  'cache' => $this->cache,
93  ]
94  );
95  }
96  }
97 
98  public function testForAccountCreation( $user, $creator, array $reqs ) {
99  if ( !$this->accountCreationThrottle || !$creator->isPingLimitable() ) {
100  return \StatusValue::newGood();
101  }
102 
103  $ip = $this->manager->getRequest()->getIP();
104 
105  if ( !$this->getHookRunner()->onExemptFromAccountCreationThrottle( $ip ) ) {
106  $this->logger->debug( __METHOD__ . ": a hook allowed account creation w/o throttle" );
107  return \StatusValue::newGood();
108  }
109 
110  $result = $this->accountCreationThrottle->increase( null, $ip, __METHOD__ );
111  if ( $result ) {
112  $message = wfMessage( 'acct_creation_throttle_hit' )->params( $result['count'] )
113  ->durationParams( $result['wait'] );
114  return \StatusValue::newFatal( $message );
115  }
116 
117  return \StatusValue::newGood();
118  }
119 
120  public function testForAuthentication( array $reqs ) {
121  if ( !$this->passwordAttemptThrottle ) {
122  return \StatusValue::newGood();
123  }
124 
125  $ip = $this->manager->getRequest()->getIP();
126  try {
128  } catch ( \UnexpectedValueException $e ) {
129  $username = null;
130  }
131 
132  // Get everything this username could normalize to, and throttle each one individually.
133  // If nothing uses usernames, just throttle by IP.
134  if ( $username !== null ) {
135  $usernames = $this->manager->normalizeUsername( $username );
136  } else {
137  $usernames = [ null ];
138  }
139  $result = false;
140  foreach ( $usernames as $name ) {
141  $r = $this->passwordAttemptThrottle->increase( $name, $ip, __METHOD__ );
142  if ( $r && ( !$result || $result['wait'] < $r['wait'] ) ) {
143  $result = $r;
144  }
145  }
146 
147  if ( $result ) {
148  $message = wfMessage( 'login-throttled' )->durationParams( $result['wait'] );
149  return \StatusValue::newFatal( $message );
150  } else {
151  $this->manager->setAuthenticationSessionData( 'LoginThrottle',
152  [ 'users' => $usernames, 'ip' => $ip ] );
153  return \StatusValue::newGood();
154  }
155  }
156 
161  public function postAuthentication( $user, AuthenticationResponse $response ) {
162  if ( $response->status !== AuthenticationResponse::PASS ) {
163  return;
164  } elseif ( !$this->passwordAttemptThrottle ) {
165  return;
166  }
167 
168  $data = $this->manager->getAuthenticationSessionData( 'LoginThrottle' );
169  if ( !$data ) {
170  // this can occur when login is happening via AuthenticationRequest::$loginRequest
171  // so testForAuthentication is skipped
172  $this->logger->info( 'throttler data not found for {user}', [ 'user' => $user->getName() ] );
173  return;
174  }
175 
176  foreach ( $data['users'] as $name ) {
177  $this->passwordAttemptThrottle->clear( $name, $data['ip'] );
178  }
179  }
180 }
MediaWiki\Auth\ThrottlePreAuthenticationProvider\$passwordAttemptThrottle
Throttler $passwordAttemptThrottle
Definition: ThrottlePreAuthenticationProvider.php:44
MediaWiki\Auth\ThrottlePreAuthenticationProvider\postAuthentication
postAuthentication( $user, AuthenticationResponse $response)
Definition: ThrottlePreAuthenticationProvider.php:161
MediaWiki\Auth\ThrottlePreAuthenticationProvider\$throttleSettings
array $throttleSettings
Definition: ThrottlePreAuthenticationProvider.php:38
ObjectCache\getLocalClusterInstance
static getLocalClusterInstance()
Get the main cluster-local cache object.
Definition: ObjectCache.php:273
MediaWiki\Auth\ThrottlePreAuthenticationProvider\$accountCreationThrottle
Throttler $accountCreationThrottle
Definition: ThrottlePreAuthenticationProvider.php:41
MediaWiki\Auth\ThrottlePreAuthenticationProvider\postInitSetup
postInitSetup()
A provider can override this to do any necessary setup after init() is called.
Definition: ThrottlePreAuthenticationProvider.php:63
BagOStuff
Class representing a cache/ephemeral data store.
Definition: BagOStuff.php:86
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1186
MediaWiki\Auth\Throttler
Definition: Throttler.php:37
MediaWiki\Auth\ThrottlePreAuthenticationProvider\$cache
BagOStuff $cache
Definition: ThrottlePreAuthenticationProvider.php:47
MediaWiki\Auth\AuthenticationRequest\getUsernameFromRequests
static getUsernameFromRequests(array $reqs)
Get the username from the set of requests.
Definition: AuthenticationRequest.php:292
MediaWiki\Auth\AuthenticationResponse
This is a value object to hold authentication response data.
Definition: AuthenticationResponse.php:37
MediaWiki\Auth\ThrottlePreAuthenticationProvider\__construct
__construct( $params=[])
Definition: ThrottlePreAuthenticationProvider.php:57
MediaWiki\Auth\ThrottlePreAuthenticationProvider
A pre-authentication provider to throttle authentication actions.
Definition: ThrottlePreAuthenticationProvider.php:36
MediaWiki\Auth\ThrottlePreAuthenticationProvider\testForAuthentication
testForAuthentication(array $reqs)
Determine whether an authentication may begin.Called from AuthManager::beginAuthentication()StatusVal...
Definition: ThrottlePreAuthenticationProvider.php:120
MediaWiki\Auth\AbstractPreAuthenticationProvider
A base class that implements some of the boilerplate for a PreAuthenticationProvider.
Definition: AbstractPreAuthenticationProvider.php:33
MediaWiki\Auth\AuthenticationResponse\PASS
const PASS
Indicates that the authentication succeeded.
Definition: AuthenticationResponse.php:39
MediaWiki\Auth
Definition: AbstractAuthenticationProvider.php:22
MediaWiki\Auth\ThrottlePreAuthenticationProvider\testForAccountCreation
testForAccountCreation( $user, $creator, array $reqs)
Determine whether an account creation may begin.Called from AuthManager::beginAccountCreation()No nee...
Definition: ThrottlePreAuthenticationProvider.php:98
MediaWiki\Auth\AbstractAuthenticationProvider\getHookRunner
getHookRunner()
Definition: AbstractAuthenticationProvider.php:171