Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 27 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
LoginAttemptCounter | |
0.00% |
0 / 27 |
|
0.00% |
0 / 7 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
increaseBadLoginCounter | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
resetBadLoginCounter | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
isBadLoginTriggered | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
isBadLoginPerUserTriggered | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
badLoginKey | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
badLoginPerUserKey | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\ConfirmEdit\Auth; |
4 | |
5 | use BagOStuff; |
6 | use MediaWiki\Extension\ConfirmEdit\CaptchaTriggers; |
7 | use MediaWiki\Extension\ConfirmEdit\SimpleCaptcha\SimpleCaptcha; |
8 | use MediaWiki\MediaWikiServices; |
9 | use MediaWiki\User\UserNameUtils; |
10 | use ObjectCache; |
11 | use User; |
12 | |
13 | /** |
14 | * Helper to count login attempts per IP and per username. |
15 | * |
16 | * @internal |
17 | */ |
18 | class LoginAttemptCounter { |
19 | private SimpleCaptcha $captcha; |
20 | |
21 | public function __construct( SimpleCaptcha $captcha ) { |
22 | $this->captcha = $captcha; |
23 | } |
24 | |
25 | /** |
26 | * Increase bad login counter after a failed login. |
27 | * The user might be required to solve a captcha if the count is high. |
28 | * @param string $username |
29 | * TODO use Throttler |
30 | */ |
31 | public function increaseBadLoginCounter( $username ) { |
32 | global $wgCaptchaBadLoginExpiration, $wgCaptchaBadLoginPerUserExpiration; |
33 | |
34 | $cache = ObjectCache::getLocalClusterInstance(); |
35 | |
36 | if ( $this->captcha->triggersCaptcha( CaptchaTriggers::BAD_LOGIN ) ) { |
37 | $key = $this->badLoginKey( $cache ); |
38 | $cache->incrWithInit( $key, $wgCaptchaBadLoginExpiration ); |
39 | } |
40 | |
41 | if ( $this->captcha->triggersCaptcha( CaptchaTriggers::BAD_LOGIN_PER_USER ) && $username ) { |
42 | $key = $this->badLoginPerUserKey( $username, $cache ); |
43 | $cache->incrWithInit( $key, $wgCaptchaBadLoginPerUserExpiration ); |
44 | } |
45 | } |
46 | |
47 | /** |
48 | * Reset bad login counter after a successful login. |
49 | * @param string $username |
50 | */ |
51 | public function resetBadLoginCounter( $username ) { |
52 | if ( $this->captcha->triggersCaptcha( CaptchaTriggers::BAD_LOGIN_PER_USER ) && $username ) { |
53 | $cache = ObjectCache::getLocalClusterInstance(); |
54 | $cache->delete( $this->badLoginPerUserKey( $username, $cache ) ); |
55 | } |
56 | } |
57 | |
58 | /** |
59 | * Check if a bad login has already been registered for this |
60 | * IP address. If so, require a captcha. |
61 | * @return bool |
62 | */ |
63 | public function isBadLoginTriggered() { |
64 | global $wgCaptchaBadLoginAttempts; |
65 | |
66 | $cache = ObjectCache::getLocalClusterInstance(); |
67 | return $this->captcha->triggersCaptcha( CaptchaTriggers::BAD_LOGIN ) |
68 | && (int)$cache->get( $this->badLoginKey( $cache ) ) >= $wgCaptchaBadLoginAttempts; |
69 | } |
70 | |
71 | /** |
72 | * Is the per-user captcha triggered? |
73 | * |
74 | * @param User|string $u User object, or name |
75 | * @return bool |
76 | */ |
77 | public function isBadLoginPerUserTriggered( $u ) { |
78 | global $wgCaptchaBadLoginPerUserAttempts; |
79 | |
80 | $cache = ObjectCache::getLocalClusterInstance(); |
81 | |
82 | if ( is_object( $u ) ) { |
83 | $u = $u->getName(); |
84 | } |
85 | $badLoginPerUserKey = $this->badLoginPerUserKey( $u, $cache ); |
86 | return $this->captcha->triggersCaptcha( CaptchaTriggers::BAD_LOGIN_PER_USER ) |
87 | && (int)$cache->get( $badLoginPerUserKey ) >= $wgCaptchaBadLoginPerUserAttempts; |
88 | } |
89 | |
90 | /** |
91 | * Internal cache key for badlogin checks. |
92 | * @param BagOStuff $cache |
93 | * @return string |
94 | */ |
95 | private function badLoginKey( BagOStuff $cache ) { |
96 | global $wgRequest; |
97 | $ip = $wgRequest->getIP(); |
98 | |
99 | return $cache->makeGlobalKey( 'captcha', 'badlogin', 'ip', $ip ); |
100 | } |
101 | |
102 | /** |
103 | * Cache key for badloginPerUser checks. |
104 | * @param string $username |
105 | * @param BagOStuff $cache |
106 | * @return string |
107 | */ |
108 | private function badLoginPerUserKey( $username, BagOStuff $cache ) { |
109 | $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils(); |
110 | $username = $userNameUtils->getCanonical( $username, UserNameUtils::RIGOR_USABLE ) ?: $username; |
111 | |
112 | return $cache->makeGlobalKey( |
113 | 'captcha', 'badlogin', 'user', md5( $username ) |
114 | ); |
115 | } |
116 | } |