Plugin Directory

Changeset 730189


Ignore:
Timestamp:
06/23/2013 02:09:12 AM (13 years ago)
Author:
convissor
Message:

Squash for release 0.40.0.

Location:
login-security-solution/trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • login-security-solution/trunk/login-security-solution.php

    r719657 r730189  
    77 *
    88 * Plugin URI: http://wordpress.org/extend/plugins/login-security-solution/
    9  * Version: 0.39.0
     9 * Version: 0.40.0
    1010 *         (Remember to change the VERSION constant, below, as well!)
    1111 * Author: Daniel Convissor
     
    4343     * This plugin's version
    4444     */
    45     const VERSION = '0.39.0';
     45    const VERSION = '0.40.0';
    4646
    4747    /**
     
    6969    const LOGIN_FORCE_PW_CHANGE = 2;
    7070    const LOGIN_NOTIFY = 4;
    71     const LOGIN_VERIFIED_IP = 8;
     71    const LOGIN_VERIFIED_IP_SAFE = 8;
    7272    const LOGIN_UNKNOWN_IP = 16;
    7373    const LOGIN_CLEAN = 32;
     74    const LOGIN_VERIFIED_IP_NEW = 64;
     75    const LOGIN_VERIFIED_IP_OLD = 128;
    7476
    7577    /**
     
    847849     */
    848850    public function wp_login($user_name, $user) {
    849         ###$this->log(__FUNCTION__, $user_name);
     851        ###$this->log(__FUNCTION__, is_object($user) ? $user->user_name : 'ERROR: non-object');
    850852        return $this->process_login_success($user);
    851853    }
     
    995997     */
    996998    protected function delete_pw_force_change($user_ID) {
     999        ###$this->log(__FUNCTION__, $user_ID);
    9971000        return delete_user_meta($user_ID, $this->umk_pw_force_change);
    9981001    }
     
    11921195     */
    11931196    protected function get_pw_force_change($user_ID) {
     1197        ###$this->log(__FUNCTION__, $user_ID);
    11941198        return (bool) get_user_meta($user_ID, $this->umk_pw_force_change, true);
     1199    }
     1200
     1201    /**
     1202     * Gets the server's request time
     1203     *
     1204     * Provided for overloading by unit tests so they can create multiple
     1205     * entries in one request.
     1206     *
     1207     * @return int  $_SERVER['REQUEST_TIME']
     1208     */
     1209    protected function get_time() {
     1210        return $_SERVER['REQUEST_TIME'];
    11951211    }
    11961212
     
    22792295         * recent failed logins and the current IP address has been verified.
    22802296         */
    2281         if ($fails['network_ip'] <= $this->options['login_fail_breach_pw_force_change']
    2282             && in_array($ip, $this->get_verified_ips($user->ID)))
    2283         {
    2284             // Use <= instead of <, above, in case
    2285             // login_fail_breach_pw_force_change = 0.
    2286 
    2287             ###$this->log(__FUNCTION__, "$user->user_login verified IP");
    2288             $flag += self::LOGIN_VERIFIED_IP;
    2289             $verified_ip = true;
     2297
     2298        $ip_time = array_search($ip, $this->get_verified_ips($user->ID));
     2299        ###$this->log(__FUNCTION__, 'ip_time', $ip_time ? $ip_time : 'false');
     2300        if ($ip_time !== false) {
     2301            if ($fails['network_ip'] <= $this->options['login_fail_breach_pw_force_change'])
     2302            {
     2303                // Use <= instead of <, above, in case
     2304                // login_fail_breach_pw_force_change = 0.
     2305
     2306                // Not part of attack.
     2307                ###$this->log(__FUNCTION__, "$user->user_login verified IP, not part of attack");
     2308                $flag += self::LOGIN_VERIFIED_IP_SAFE;
     2309                $verified_ip = true;
     2310            } else {
     2311                $age = $this->get_time() - $ip_time;
     2312                $max_age_permitted = $this->options['login_fail_minutes'] * 60;
     2313                if ($age < $max_age_permitted) {
     2314                    // Same IP as "attacker," but IP verified recently.
     2315                    ###$this->log(__FUNCTION__, "$user->user_login, part of attack, but newly verified IP ($age < $max_age_permitted)");
     2316                    $flag += self::LOGIN_VERIFIED_IP_NEW;
     2317                    $verified_ip = true;
     2318                } else {
     2319                    // Same IP as "attacker," and IP verified a while ago.
     2320                    ###$this->log(__FUNCTION__, "$user->user_login, part of attack, old verified IP ($age >= $max_age_permitted)");
     2321                    $flag += self::LOGIN_VERIFIED_IP_OLD;
     2322                    $verified_ip = false;
     2323                }
     2324            }
    22902325        } else {
    22912326            ###$this->log(__FUNCTION__, "$user->user_login non-verified IP");
     
    24442479     * Stores the user's current IP address
    24452480     *
    2446      * Note: saves up to 10 adddresses, duplicates are not stored.
     2481     * Note: saves up to 20 adddresses, duplicates are not stored.
     2482     *
     2483     * Note: storing IP's in array values for backwards compatibility.
    24472484     *
    24482485     * @param int $user_ID  the user's id number
    24492486     * @param string $new_ip  the ip address to add
    2450      * @return mixed  true on success, 1 if IP is already stored, -1 if IP empty
     2487     * @return mixed  true on success, -1 if IP empty
    24512488     */
    24522489    protected function save_verified_ip($user_ID, $new_ip) {
     
    24562493
    24572494        $ips = $this->get_verified_ips($user_ID);
    2458 
    2459         if (in_array($new_ip, $ips)) {
    2460             return 1;
    2461         }
    2462 
    2463         $ips[] = $new_ip;
    2464 
    2465         $cut = count($ips) - 10;
    2466         if ($cut > 0) {
    2467             array_splice($ips, 0, $cut);
     2495        $time = array_search($new_ip, $ips);
     2496
     2497        if ($time !== false) {
     2498            // Replace time stamp.
     2499            unset($ips[$time]);
     2500        }
     2501        $ips[$this->get_time()] = $new_ip;
     2502
     2503        if (count($ips) > 20) {
     2504            // Drop oldest (first) element to keep array from getting to big.
     2505            // Can't use array_shift() because it reindexes the array
     2506            $first_key = key($ips);
     2507            unset($ips[$first_key]);
    24682508        }
    24692509
     
    25212561     */
    25222562    protected function set_pw_force_change($user_ID) {
     2563        ###$this->log(__FUNCTION__, $user_ID);
    25232564        return update_user_meta($user_ID, $this->umk_pw_force_change, 1);
    25242565    }
  • login-security-solution/trunk/readme.txt

    r719657 r730189  
    445445
    446446== Changelog ==
     447
     448= 0.40.0 (2013-06-22) =
     449* Track the age of verified IP's and use that to prevent users being locked
     450out by "attacks" from one's own IP address.
     451* Unit tests pass using PHP 5.3.27-dev, 5.4.17-dev, 5.5.0-dev
     452* Tested under WordPress 3.4.2, 3.5.2 and 3.6beta4 using regular and multisite.
    447453
    448454= 0.39.0 (2013-05-29) =
  • login-security-solution/trunk/tests/Accessor.php

    r705187 r730189  
    3333 */
    3434class Accessor extends login_security_solution_admin {
     35    public $time_overload = 10;
     36
    3537    public function __call($method, $args) {
    3638        return call_user_func_array(array($this, $method), $args);
     
    4547        return $this->data[$key];
    4648    }
     49    protected function get_time() {
     50        return $this->time_overload++;
     51    }
    4752}
  • login-security-solution/trunk/tests/LoginFailTest.php

    r705187 r730189  
    336336     * @depends test_wp_login__post_breach_threshold
    337337     */
    338     public function test_wp_login__post_breach_threshold_verified_ip() {
     338    public function test_wp_login__post_breach_threshold_verified_ip_safe() {
    339339        global $wpdb;
    340340
     
    353353            $this->fail($e->getMessage());
    354354        }
    355         $flag = login_security_solution::LOGIN_VERIFIED_IP;
     355        $flag = login_security_solution::LOGIN_VERIFIED_IP_SAFE;
    356356        $this->assertSame($flag + 1, $actual, 'wp_login() return value...');
    357357        $this->assertNull(self::$lss->sleep, 'Sleep should be unset.');
     
    359359        $actual = self::$lss->get_pw_force_change($this->user->ID);
    360360        $this->assertFalse($actual, 'get_pw_force_change() return value...');
    361 
     361    }
     362
     363    /**
     364     * @depends test_wp_login__post_breach_threshold_verified_ip_safe
     365     */
     366    public function test_wp_login__post_breach_threshold_verified_ip_new() {
     367        self::$lss->delete_pw_force_change($this->user->ID);
     368
     369        $options = self::$lss->options;
     370        $options['login_fail_breach_pw_force_change'] = 1;
     371        $options['login_fail_minutes'] = 1;
     372        self::$lss->options = $options;
     373
     374        self::$lss->time_overload = 10;
     375        self::$lss->save_verified_ip($this->user->ID, $this->ip);
     376        self::$lss->time_overload = 20;
     377
     378        try {
     379            // Do THE deed.
     380            $actual = self::$lss->wp_login(null, $this->user);
     381        } catch (Exception $e) {
     382            $this->fail($e->getMessage());
     383        }
     384        $flag = login_security_solution::LOGIN_VERIFIED_IP_NEW;
     385        $this->assertSame($flag + 1, $actual, 'wp_login() return value...');
     386        $this->assertNull(self::$lss->sleep, 'Sleep should be unset.');
     387
     388        $actual = self::$lss->get_pw_force_change($this->user->ID);
     389        $this->assertFalse($actual, 'get_pw_force_change() return value...');
     390    }
     391
     392    /**
     393     * @depends test_wp_login__post_breach_threshold_verified_ip_new
     394     */
     395    public function test_wp_login__post_breach_threshold_verified_ip_old() {
     396        global $wpdb;
     397
     398        self::$lss->delete_pw_force_change($this->user->ID);
     399
     400        $options = self::$lss->options;
     401        $options['login_fail_breach_notify'] = 0;
     402        $options['login_fail_breach_pw_force_change'] = 1;
     403        $options['login_fail_minutes'] = 1;
     404        self::$lss->options = $options;
     405
     406        self::$lss->time_overload = 10;
     407        self::$lss->save_verified_ip($this->user->ID, $this->ip);
     408        self::$lss->time_overload = 100;
     409
     410        try {
     411            // Do THE deed.
     412            $actual = self::$lss->wp_login(null, $this->user);
     413        } catch (Exception $e) {
     414            $this->fail($e->getMessage());
     415        }
     416        $flag = login_security_solution::LOGIN_VERIFIED_IP_OLD
     417                + login_security_solution::LOGIN_FORCE_PW_CHANGE;
     418        $this->assertSame($flag + 1, $actual, 'wp_login() return value...');
     419        $this->assertGreaterThan(0, self::$lss->sleep, 'Sleep not set.');
     420
     421        $actual = self::$lss->get_pw_force_change($this->user->ID);
     422        $this->assertTrue($actual, 'get_pw_force_change() return value...');
     423
     424        self::$lss->delete_pw_force_change($this->user->ID);
    362425        $wpdb->query('ROLLBACK TO pre_verified_ip');
    363426    }
    364427
    365428    /**
    366      * @depends test_wp_login__post_breach_threshold_verified_ip
     429     * @depends test_wp_login__post_breach_threshold_verified_ip_old
    367430     */
    368431    public function test_wp_login__post_breach_threshold_only_notify() {
     
    372435        $options['login_fail_breach_pw_force_change'] = 0;
    373436        self::$lss->options = $options;
    374 
    375         self::$lss->delete_pw_force_change($this->user->ID);
    376437
    377438        try {
     
    405466
    406467        self::$lss->delete_pw_force_change($this->user->ID);
     468        $actual = self::$lss->get_pw_force_change($this->user->ID);
     469        $this->assertFalse($actual, 'pw_force_change should be empty to start with');
     470        delete_user_meta($this->user->ID, self::$lss->umk_verified_ips);
    407471
    408472        try {
  • login-security-solution/trunk/tests/VerifiedIpTest.php

    r615063 r730189  
    3535    public function setUp() {
    3636        parent::setUp();
     37        // Because arrays naturally start at index 0, force our IP array to
     38        // start indexing at 10 to ensure tests do what we really expect
     39        // than passing by getting lucky.
     40        self::$lss->time_overload = 10;
    3741    }
    3842
     
    6670    public function test_save_verified_ip__exists() {
    6771        $actual = self::$lss->save_verified_ip($this->user->ID, self::$ip_1);
    68         $this->assertSame(1, $actual);
     72        $this->assertTrue($actual);
    6973    }
    7074
     
    7680
    7781        $actual = self::$lss->get_verified_ips($this->user->ID);
    78         $this->assertEquals(array(self::$ip_1), $actual);
     82        $this->assertEquals(array(10 => self::$ip_1), $actual);
    7983
    8084        $wpdb->query('ROLLBACK TO empty');
     
    102106        self::$lss->save_verified_ip($this->user->ID, 'j');
    103107        self::$lss->save_verified_ip($this->user->ID, 'k');
     108        self::$lss->save_verified_ip($this->user->ID, 'l');
     109        self::$lss->save_verified_ip($this->user->ID, 'm');
     110        self::$lss->save_verified_ip($this->user->ID, 'n');
     111        self::$lss->save_verified_ip($this->user->ID, 'o');
     112        self::$lss->save_verified_ip($this->user->ID, 'p');
     113        self::$lss->save_verified_ip($this->user->ID, 'q');
     114        self::$lss->save_verified_ip($this->user->ID, 'r');
     115        self::$lss->save_verified_ip($this->user->ID, 's');
     116        self::$lss->save_verified_ip($this->user->ID, 't');
     117        self::$lss->save_verified_ip($this->user->ID, 'u');
    104118
    105         $expected = range('b', 'k');
     119        $expected_keys = range(11, 30);
     120        $expected_values = range('b', 'u');
     121        $expected = array_combine($expected_keys, $expected_values);
    106122        $actual = self::$lss->get_verified_ips($this->user->ID);
    107123        $this->assertEquals($expected, $actual);
     
    125141        // Check the outcome.
    126142        $actual = self::$lss->get_verified_ips($this->user->ID);
    127         $this->assertSame(array($ip), $actual, 'Expected IP was not found.');
     143        $this->assertSame(array(10 => $ip), $actual, 'Expected IP was not found.');
    128144
    129145        $wpdb->query('ROLLBACK TO empty');
     
    152168        // Check the outcome.
    153169        $actual = self::$lss->get_verified_ips($this->user->ID);
    154         $this->assertSame(array($ip), $actual, 'Expected IP was not found.');
     170        $this->assertSame(array(10 => $ip), $actual, 'Expected IP was not found.');
    155171    }
    156172}
Note: See TracChangeset for help on using the changeset viewer.