r22488 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r22487‎ | r22488 | r22489 >
Date:23:50, 27 May 2007
Author:yurik
Status:old
Tags:
Comment:
API: Enabled API login throttling (with amidaniel's help)
fixed memcached-client comments
minor queryRevisions fix
Modified paths:
  • /trunk/phase3/includes/api/ApiLogin.php (modified) (history)
  • /trunk/phase3/includes/api/ApiMain.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryRevisions.php (modified) (history)
  • /trunk/phase3/includes/memcached-client.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/api/ApiLogin.php
@@ -5,7 +5,8 @@
66 *
77 * API for MediaWiki 1.8+
88 *
9 - * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
 9+ * Copyright (C) 2006-2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com,
 10+ * Daniel Cannon (cannon dot danielc at gmail dot com)
1011 *
1112 * This program is free software; you can redistribute it and/or modify
1213 * it under the terms of the GNU General Public License as published by
@@ -29,14 +30,44 @@
3031 }
3132
3233 /**
 34+ * Unit to authenticate log-in attempts to the current wiki.
 35+ *
3336 * @addtogroup API
3437 */
3538 class ApiLogin extends ApiBase {
 39+
 40+ /**
 41+ * The amount of time a user must wait after submitting
 42+ * a bad login (will be multiplied by the THROTTLE_FACTOR for each bad attempt)
 43+ */
 44+ const THROTTLE_TIME = 10;
3645
 46+ /**
 47+ * The factor by which the wait-time in between authentication
 48+ * attempts is increased every failed attempt.
 49+ */
 50+ const THROTTLE_FACTOR = 1.5;
 51+
 52+ /**
 53+ * The maximum number of failed logins after which the wait increase stops.
 54+ */
 55+ const THOTTLE_MAX_COUNT = 10;
 56+
3757 public function __construct($main, $action) {
3858 parent :: __construct($main, $action, 'lg');
3959 }
4060
 61+ /**
 62+ * Executes the log-in attempt using the parameters passed. If
 63+ * the log-in succeeeds, it attaches a cookie to the session
 64+ * and outputs the user id, username, and session token. If a
 65+ * log-in fails, as the result of a bad password, a nonexistant
 66+ * user, or any other reason, the host is cached with an expiry
 67+ * and no log-in attempts will be accepted until that expiry
 68+ * is reached. The expiry is $this->mLoginThrottle.
 69+ *
 70+ * @access public
 71+ */
4172 public function execute() {
4273 $name = $password = $domain = null;
4374 extract($this->extractRequestParams());
@@ -50,6 +81,15 @@
5182
5283 $result = array ();
5384
 85+ $nextLoginIn = $this->getNextLoginTimeout();
 86+ if ($nextLoginIn > 0) {
 87+ $result['result'] = 'NeedToWait';
 88+ $result['details'] = "Please wait $nextLoginIn seconds before next log-in attempt";
 89+ $result['wait'] = $nextLoginIn;
 90+ $this->getResult()->addValue(null, 'login', $result);
 91+ return;
 92+ }
 93+
5494 $loginForm = new LoginForm($params);
5595 switch ($loginForm->authenticateUserData()) {
5696 case LoginForm :: SUCCESS :
@@ -86,9 +126,89 @@
87127 ApiBase :: dieDebug(__METHOD__, 'Unhandled case value');
88128 }
89129
 130+ if ($result['result'] != 'Success') {
 131+ $result['wait'] = $this->cacheBadLogin();
 132+ }
 133+ // if we were allowed to try to login, memcache is fine
 134+
90135 $this->getResult()->addValue(null, 'login', $result);
91136 }
92137
 138+
 139+ /**
 140+ * Caches a bad-login attempt associated with the host and with an
 141+ * expiry of $this->mLoginThrottle. These are cached by a key
 142+ * separate from that used by the captcha system--as such, logging
 143+ * in through the standard interface will get you a legal session
 144+ * and cookies to prove it, but will not remove this entry.
 145+ *
 146+ * Returns the number of seconds until next login attempt will be allowed.
 147+ *
 148+ * @access private
 149+ */
 150+ private function cacheBadLogin() {
 151+ global $wgMemc;
 152+
 153+ $key = $this->getMemCacheKey();
 154+ $val =& $wgMemc->get( $key );
 155+
 156+ $val['lastReqTime'] = time();
 157+ if (!isset($val['count'])) {
 158+ $val['count'] = 1;
 159+ } else {
 160+ $val['count'] = 1 + $val['count'];
 161+ }
 162+
 163+ $delay = ApiLogin::calculateDelay($val);
 164+
 165+ $wgMemc->delete($key);
 166+ $wgMemc->add( $key, $val, $delay );
 167+
 168+ return $delay;
 169+ }
 170+
 171+ /**
 172+ * How much time the client must wait before it will be
 173+ * allowed to try to log-in next.
 174+ * The return value is 0 if no wait is required.
 175+ */
 176+ private function getNextLoginTimeout() {
 177+ global $wgMemc;
 178+
 179+ $val = $wgMemc->get($this->getMemCacheKey());
 180+
 181+ $elapse = (time() - $val['lastReqTime']) / 1000; // in seconds
 182+ $canRetryIn = ApiLogin::calculateDelay($val) - $elapse;
 183+ $canRetryIn = $canRetryIn < 0 ? 0 : $canRetryIn;
 184+
 185+ return $canRetryIn;
 186+ }
 187+
 188+ /**
 189+ * Based on the number of previously attempted logins, returns
 190+ * the delay (in seconds) when the next login attempt will be allowed.
 191+ */
 192+ private static function calculateDelay($val) {
 193+ // Defensive programming
 194+ $count = $val['count'];
 195+ $count = $count < 1 ? 1 : $count;
 196+ $count = $count > self::THOTTLE_MAX_COUNT ? self::THOTTLE_MAX_COUNT : $count;
 197+
 198+ return self::THROTTLE_TIME + self::THROTTLE_TIME * ($count - 1) * self::THROTTLE_FACTOR;
 199+ }
 200+
 201+ /**
 202+ * Internal cache key for badlogin checks. Robbed from the
 203+ * ConfirmEdit extension and modified to use a key unique to the
 204+ * API login.3
 205+ *
 206+ * @return string
 207+ * @access private
 208+ */
 209+ private function getMemCacheKey() {
 210+ return wfMemcKey( 'apilogin', 'badlogin', 'ip', wfGetIP() );
 211+ }
 212+
93213 protected function getAllowedParams() {
94214 return array (
95215 'name' => null,
@@ -107,7 +227,12 @@
108228
109229 protected function getDescription() {
110230 return array (
111 - 'This module is used to login and get the authentication tokens.'
 231+ 'This module is used to login and get the authentication tokens. ' .
 232+ 'In the event of a successful log-in, a cookie will be attached ' .
 233+ 'to your session. In the event of a failed log-in, you will not ' .
 234+ 'be able to attempt another log-in through this method for 60 ' .
 235+ 'seconds--this is to prevent its use in aiding automated password ' .
 236+ 'crackers.'
112237 );
113238 }
114239
Index: trunk/phase3/includes/api/ApiMain.php
@@ -52,7 +52,7 @@
5353 * List of available modules: action name => module class
5454 */
5555 private static $Modules = array (
56 -// 'login' => 'ApiLogin', // LOGIN is temporarily disabled until it becomes more secure
 56+ 'login' => 'ApiLogin',
5757 'query' => 'ApiQuery',
5858 'opensearch' => 'ApiOpenSearch',
5959 'feedwatchlist' => 'ApiFeedWatchlist',
Index: trunk/phase3/includes/api/ApiQueryRevisions.php
@@ -134,7 +134,7 @@
135135 $this->addWhereFld('rev_page', current(array_keys($pageSet->getGoodTitles())));
136136
137137 if(!is_null($user)) {
138 - $this->addWhere('rev_user_text =' . $this->getDB()->addQuotes($user));
 138+ $this->addWhereFld('rev_user_text', $user);
139139 } elseif (!is_null( $excludeuser)) {
140140 $this->addWhere('rev_user_text != ' . $this->getDB()->addQuotes($excludeuser));
141141 }
Index: trunk/phase3/includes/memcached-client.php
@@ -152,7 +152,7 @@
153153 /**
154154 * At how many bytes should we compress?
155155 *
156 - * @var interger
 156+ * @var integer
157157 * @access private
158158 */
159159 var $_compress_threshold;
@@ -192,7 +192,7 @@
193193 /**
194194 * Total # of bit buckets we have
195195 *
196 - * @var interger
 196+ * @var integer
197197 * @access private
198198 */
199199 var $_bucketcount;
@@ -200,7 +200,7 @@
201201 /**
202202 * # of total servers we have
203203 *
204 - * @var interger
 204+ * @var integer
205205 * @access private
206206 */
207207 var $_active;
@@ -272,9 +272,9 @@
273273 * Adds a key/value to the memcache server if one isn't already set with
274274 * that key
275275 *
276 - * @param string $key Key to set with data
277 - * @param mixed $val Value to store
278 - * @param interger $exp (optional) Time to expire data at
 276+ * @param string $key Key to set with data
 277+ * @param mixed $val Value to store
 278+ * @param integer $exp (optional) Time to expire data at
279279 *
280280 * @return boolean
281281 * @access public
@@ -291,7 +291,7 @@
292292 * Decriment a value stored on the memcache server
293293 *
294294 * @param string $key Key to decriment
295 - * @param interger $amt (optional) Amount to decriment
 295+ * @param integer $amt (optional) Amount to decriment
296296 *
297297 * @return mixed FALSE on failure, value on success
298298 * @access public
@@ -308,7 +308,7 @@
309309 * Deletes a key from the server, optionally after $time
310310 *
311311 * @param string $key Key to delete
312 - * @param interger $time (optional) How long to wait before deleting
 312+ * @param integer $time (optional) How long to wait before deleting
313313 *
314314 * @return boolean TRUE on success, FALSE on failure
315315 * @access public
@@ -506,9 +506,9 @@
507507 * Increments $key (optionally) by $amt
508508 *
509509 * @param string $key Key to increment
510 - * @param interger $amt (optional) amount to increment
 510+ * @param integer $amt (optional) amount to increment
511511 *
512 - * @return interger New key value?
 512+ * @return integer New key value?
513513 * @access public
514514 */
515515 function incr ($key, $amt=1)
@@ -524,7 +524,7 @@
525525 *
526526 * @param string $key Key to set value as
527527 * @param mixed $value Value to store
528 - * @param interger $exp (optional) Experiation time
 528+ * @param integer $exp (optional) Experiation time
529529 *
530530 * @return boolean
531531 * @access public
@@ -582,7 +582,7 @@
583583 *
584584 * @param string $key Key to set value as
585585 * @param mixed $value Value to set
586 - * @param interger $exp (optional) Experiation time
 586+ * @param integer $exp (optional) Experiation time
587587 *
588588 * @return boolean TRUE on success
589589 * @access public
@@ -598,7 +598,7 @@
599599 /**
600600 * Sets the compression threshold
601601 *
602 - * @param interger $thresh Threshold to compress if larger than
 602+ * @param integer $thresh Threshold to compress if larger than
603603 *
604604 * @access public
605605 */
@@ -687,7 +687,7 @@
688688 /**
689689 * Connects $sock to $host, timing out after $timeout
690690 *
691 - * @param interger $sock Socket to connect
 691+ * @param integer $sock Socket to connect
692692 * @param string $host Host:IP to connect to
693693 *
694694 * @return boolean
@@ -807,11 +807,11 @@
808808 // {{{ _hashfunc()
809809
810810 /**
811 - * Creates a hash interger based on the $key
 811+ * Creates a hash integer based on the $key
812812 *
813813 * @param string $key Key to hash
814814 *
815 - * @return interger Hash value
 815+ * @return integer Hash value
816816 * @access private
817817 */
818818 function _hashfunc ($key)
@@ -830,9 +830,9 @@
831831 *
832832 * @param string $cmd Command to perform
833833 * @param string $key Key to perform it on
834 - * @param interger $amt Amount to adjust
 834+ * @param integer $amt Amount to adjust
835835 *
836 - * @return interger New value of $key
 836+ * @return integer New value of $key
837837 * @access private
838838 */
839839 function _incrdecr ($cmd, $key, $amt=1)
@@ -929,7 +929,7 @@
930930 * @param string $cmd Command to perform
931931 * @param string $key Key to act on
932932 * @param mixed $val What we need to store
933 - * @param interger $exp When it should expire
 933+ * @param integer $exp When it should expire
934934 *
935935 * @return boolean
936936 * @access private

Follow-up revisions

RevisionCommit summaryAuthorDate
r22518Merged revisions 22484-22517 via svnmerge from...david22:22, 28 May 2007