Plugin Directory

Changeset 2124103


Ignore:
Timestamp:
07/16/2019 05:26:35 PM (7 years ago)
Author:
wfryan
Message:

1.0.3 - July 16, 2019

  • Improvement: Added additional information about reCAPTCHA to its setting control.
  • Improvement: Added a constant that may be overridden to customize the expiration time of login verification email links.
  • Improvement: reCAPTCHA keys are now tested on saving to prevent accidentally inputting a v2 key.
  • Improvement: Added a setting to control the reCAPTCHA human/bot threshold.
  • Improvement: Added an option to trigger removal of Login Security tables and data on deactivation.
  • Improvement: Reworked the reCAPTCHA implementation to trigger the token check on login/registration form submission to avoid the token expiring.
  • Fix: Widened the reCAPTCHA key fields to allow the full keys to be visible.
  • Fix: Addressed an issue when outbound UDP connections are blocked where the NTP check could log an error.
  • Fix: Added handling for reCAPTCHA's JavaScript failing to load, which previously blocked logging in.
  • Fix: Fixed the functionality of the button to send 2FA grace period notifications.
  • Fix: Fixed a missing icon for some help links when running in standalone mode.
Location:
wordfence-login-security
Files:
44 added
36 deleted
26 edited
1 copied

Legend:

Unmodified
Added
Removed
  • wordfence-login-security/tags/1.0.3/classes/controller/ajax.php

    r2098090 r2124103  
    7373                'required_parameters' => array('nonce', 'id'),
    7474            ),
     75            'reset_recaptcha_stats' => array(
     76                'handler' => array($this, '_ajax_reset_recaptcha_stats_callback'),
     77                'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => __('You do not have permission to reset reCAPTCHA statistics.', 'wordfence-2fa')),
     78                'required_parameters' => array('nonce'),
     79            ),
    7580        );
    7681       
     
    400405        foreach ($admins as $a) {
    401406            /** @var \WP_User $a */
    402             if (!Controller_Users::shared()->has_2fa_active($a)) {
     407            if (Controller_Users::shared()->has_2fa_active($a)) {
    403408                continue;
    404409            }
     
    447452        Controller_Notices::shared()->remove_notice($_POST['id'], false, wp_get_current_user());
    448453    }
     454   
     455    public function _ajax_reset_recaptcha_stats_callback() {
     456        Controller_Settings::shared()->set_array(Controller_Settings::OPTION_CAPTCHA_STATS, array('counts' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'avg' => 0));
     457        $response = array('success' => true);
     458        return die(json_encode($response));
     459    }
    449460}
  • wordfence-login-security/tags/1.0.3/classes/controller/captcha.php

    r2098090 r2124103  
    112112     */
    113113    public function is_human($score) {
     114        if (Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_CAPTCHA_TEST_MODE)) {
     115            return true;
     116        }
     117       
    114118        $threshold = $this->threshold();
    115119        return ($score >= $threshold || abs($score - $threshold) < 0.0001);
  • wordfence-login-security/tags/1.0.3/classes/controller/db.php

    r2098090 r2124103  
    5959    }
    6060   
     61    public function uninstall() {
     62        $tables = array(self::TABLE_2FA_SECRETS, self::TABLE_SETTINGS);
     63        foreach ($tables as $table) {
     64            global $wpdb;
     65            $wpdb->query('DROP TABLE IF EXISTS `' . self::network_table($table) . '`');
     66        }
     67    }
     68   
    6169    protected function _create_schema() {
    6270        $tables = array(
  • wordfence-login-security/tags/1.0.3/classes/controller/settings.php

    r2098090 r2124103  
    1919    const OPTION_ALLOW_XML_RPC = 'allow-xml-rpc';
    2020    const OPTION_ENABLE_AUTH_CAPTCHA = 'enable-auth-captcha';
     21    const OPTION_CAPTCHA_TEST_MODE = 'recaptcha-test-mode';
    2122    const OPTION_RECAPTCHA_SITE_KEY = 'recaptcha-site-key';
    2223    const OPTION_RECAPTCHA_SECRET = 'recaptcha-secret';
    2324    const OPTION_RECAPTCHA_THRESHOLD = 'recaptcha-threshold';
     25    const OPTION_DELETE_ON_DEACTIVATION = 'delete-deactivation';
    2426   
    2527    //Internal
     
    3133    const OPTION_SHARED_SYMMETRIC_SECRET_KEY = 'shared-symmetric-secret';
    3234    const OPTION_DISMISSED_FRESH_INSTALL_MODAL = 'dismissed-fresh-install-modal';
     35    const OPTION_CAPTCHA_STATS = 'captcha-stats';
    3336   
    3437    protected $_settingsStorage;
     
    6770            self::OPTION_ALLOW_XML_RPC => array('value' => true, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
    6871            self::OPTION_ENABLE_AUTH_CAPTCHA => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
     72            self::OPTION_CAPTCHA_STATS => array('value' => '{"counts":[0,0,0,0,0,0,0,0,0,0,0],"avg":0}', 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
    6973            self::OPTION_RECAPTCHA_THRESHOLD => array('value' => 0.5, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
    7074            self::OPTION_LAST_SECRET_REFRESH => array('value' => 0, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
     75            self::OPTION_DELETE_ON_DEACTIVATION => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
    7176        ));
    7277    }
     
    130135            case self::OPTION_ALLOW_XML_RPC:
    131136            case self::OPTION_ENABLE_AUTH_CAPTCHA:
     137            case self::OPTION_CAPTCHA_TEST_MODE:
    132138            case self::OPTION_DISMISSED_FRESH_INSTALL_MODAL:
     139            case self::OPTION_DELETE_ON_DEACTIVATION:
    133140                return true;
    134141               
     
    139146            //Array
    140147            case self::OPTION_GLOBAL_NOTICES:
     148            case self::OPTION_CAPTCHA_STATS:
    141149                return preg_match('/^\[.*\]$/', $value) || preg_match('/^\{.*\}$/', $value); //Only a rough JSON validation
    142150               
     
    166174            case self::OPTION_RECAPTCHA_THRESHOLD:
    167175                return is_numeric($value) && $value >= 0 && $value <= 1;
     176            case self::OPTION_RECAPTCHA_SITE_KEY:
     177                if (empty($value)) {
     178                    return true;
     179                }
     180               
     181                $response = wp_remote_get('https://www.google.com/recaptcha/api.js?render=' . urlencode($value));
     182               
     183                if (!is_wp_error($response)) {
     184                    $status = wp_remote_retrieve_response_code($response);
     185                    if ($status == 200) {
     186                        return true;
     187                    }
     188                   
     189                    $data = wp_remote_retrieve_body($response);
     190                    if (strpos($data, 'grecaptcha') === false) {
     191                        return __('Unable to validate the reCAPTCHA site key. Please check the key and try again.', 'wordfence-2fa');
     192                    }
     193                    return true;
     194                }
     195                return sprintf(__('An error was encountered while validating the reCAPTCHA site key: %s', 'wordfence-2fa'), $response->get_error_message());
    168196        }
    169197        return true;
     
    202230            case self::OPTION_ALLOW_XML_RPC:
    203231            case self::OPTION_ENABLE_AUTH_CAPTCHA:
     232            case self::OPTION_CAPTCHA_TEST_MODE:
    204233            case self::OPTION_DISMISSED_FRESH_INSTALL_MODAL:
     234            case self::OPTION_DELETE_ON_DEACTIVATION:
    205235                return $this->_truthy_to_bool($value);
    206236               
  • wordfence-login-security/tags/1.0.3/classes/controller/time.php

    r2098090 r2124103  
    9696        foreach ($servers as $s) {
    9797            $socket = @fsockopen('udp://' . $s, 123, $err_no, $err_str, 1);
    98             stream_set_timeout($socket, 1);
    9998            if ($socket) {
     99                stream_set_timeout($socket, 1);
    100100                $remote_originate = microtime(true);
    101101                $secondsNTP = ((int) $remote_originate) + self::NTP_EPOCH_CONVERT;
  • wordfence-login-security/tags/1.0.3/classes/controller/users.php

    r2098090 r2124103  
    312312   
    313313    /**
     314     * Records the reCAPTCHA score for later display.
     315     *
     316     * This is not atomic, which means this can miscount on hits that overlap, but the overhead of being atomic is not
     317     * worth it for our use.
     318     *
     319     * @param \WP_User $user|null
     320     * @param float $score
     321     */
     322    public function record_captcha_score($user, $score) {
     323        if (!Controller_CAPTCHA::shared()->enabled()) { return; }
     324        if ($this->has_2fa_active($user)) { return; } //2FA activated users do not retrieve a score
     325       
     326        if ($user) { update_user_meta($user->ID, 'wfls-last-captcha-score', $score); }
     327        $stats = Controller_Settings::shared()->get_array(Controller_Settings::OPTION_CAPTCHA_STATS);
     328        $int_score = min(max((int) ($score * 10), 0), 10);
     329        $count = array_sum($stats['counts']);
     330        $stats['counts'][$int_score]++;
     331        $stats['avg'] = ($stats['avg'] * $count + $int_score) / ($count + 1);
     332        Controller_Settings::shared()->set_array(Controller_Settings::OPTION_CAPTCHA_STATS, $stats);
     333    }
     334   
     335    /**
    314336     * Returns the active and inactive user counts.
    315337     *
  • wordfence-login-security/tags/1.0.3/classes/controller/wordfencels.php

    r2098090 r2124103  
    125125            }
    126126        }
     127       
     128        if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_CAPTCHA_TEST_MODE) && Controller_CAPTCHA::shared()->enabled() && Controller_Permissions::shared()->can_manage_settings()) {
     129            if (is_multisite()) {
     130                add_action('network_admin_notices', array($this, '_recaptcha_test_notice'));
     131            }
     132            else {
     133                add_action('admin_notices', array($this, '_recaptcha_test_notice'));
     134            }
     135        }
    127136    }
    128137   
     
    135144    }
    136145   
     146    public function _recaptcha_test_notice() {
     147        echo '<div class="notice notice-warning"><p>' . sprintf(__('reCAPTCHA test mode is enabled. While enabled, login and registration requests will be checked for their score but will not be blocked if the score is below the minimum score. <a href="%s">Manage Settings</a>', 'wordfence-2fa'), esc_url(network_admin_url('admin.php?page=WFLS#top#settings'))) . '</p></div>';
     148    }
     149   
    137150    /**
    138151     * Installation/Uninstallation
     
    151164            }
    152165            delete_option($opt);
     166        }
     167       
     168        if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_DELETE_ON_DEACTIVATION)) {
     169            Controller_DB::shared()->uninstall();
    153170        }
    154171    }
     
    243260            }
    244261            wp_enqueue_script('wordfence-ls-admin', Model_Asset::js('admin.js'), array('jquery'), WORDFENCE_LS_VERSION);
     262            if (!WORDFENCE_LS_FROM_CORE) {
     263                wp_register_script('chart-js', Model_Asset::js('Chart.bundle.min.js'), array('jquery'), '2.4.0');
     264                wp_register_script('wordfence-select2-js', Model_Asset::js('wfselect2.min.js'), array('jquery'), WORDFENCE_LS_VERSION);
     265                wp_register_style('wordfence-select2-css', Model_Asset::css('wfselect2.min.css'), array(), WORDFENCE_LS_VERSION);
     266            }
     267            wp_enqueue_script('chart-js');
     268            wp_enqueue_script('wordfence-select2-js');
     269            wp_enqueue_style('wordfence-select2-css');
    245270            wp_enqueue_style('wordfence-ls-admin', Model_Asset::css('admin.css'), array(), WORDFENCE_LS_VERSION);
    246271            wp_enqueue_style('wordfence-ls-colorbox', Model_Asset::css('colorbox.css'), array(), WORDFENCE_LS_VERSION);
    247272            wp_enqueue_style('wordfence-ls-ionicons', Model_Asset::css('ionicons.css'), array(), WORDFENCE_LS_VERSION);
     273            if (!WORDFENCE_LS_FROM_CORE) { wp_enqueue_style('wordfence-ls-font-awesome', Model_Asset::css('font-awesome.css'), array(), WORDFENCE_LS_VERSION); }
    248274            wp_localize_script('wordfence-ls-admin', 'WFLSVars', array(
    249275                'ajaxurl' => admin_url('admin-ajax.php'),
     
    400426                }
    401427               
    402                 update_user_meta($user->ID, 'wfls-last-captcha-score', $score);
     428                Controller_Users::shared()->record_captcha_score($user, $score);
    403429               
    404430                if (isset($_REQUEST['wfls-email-verification']) && !empty($_REQUEST['wfls-email-verification']) && is_string($_REQUEST['wfls-email-verification'])) {
     
    422448                    $encrypted = Model_Symmetric::encrypt((string) $user->ID);
    423449                    if ($encrypted) {
    424                         $jwt = new Model_JWT(array('user' => $encrypted), Controller_Time::time() + 60 * 15 /* minutes */);
     450                        $jwt = new Model_JWT(array('user' => $encrypted), Controller_Time::time() + 60 * WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES);
    425451                        $view = new Model_View('email/login-verification', array(
    426452                            'siteName' => get_bloginfo('name', 'raw'),
     
    618644       
    619645        if ($requireCAPTCHA) {
     646            Controller_Users::shared()->record_captcha_score(null, $score);
     647           
    620648            if (!Controller_CAPTCHA::shared()->is_human($score)) { //Score is below the human threshold, block the user registration
    621649                $encryptedIP = Model_Symmetric::encrypt(Model_Request::current()->ip());
  • wordfence-login-security/tags/1.0.3/classes/model/settings/db.php

    r2098090 r2124103  
    2626        if ($wpdb->query($wpdb->prepare("INSERT INTO `{$table}` (`name`, `value`, `autoload`) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`), `autoload` = VALUES(`autoload`)", $key, $value, $autoload)) !== false && $autoload != self::AUTOLOAD_NO) {
    2727            $this->_update_cached($key, $value);
     28            do_action('wfls_settings_set', $key, $value);
    2829        }
    2930    }
  • wordfence-login-security/tags/1.0.3/readme.txt

    r2098090 r2124103  
    11=== Wordfence Login Security ===
    2 Contributors: wfryan 
     2Contributors: wfryan, wfmattr, mmaunder, wfmatt
    33Tags: security, login security, 2fa, two factor authentication, captcha, xml-rpc, mfa, 2 factor
    44Requires at least: 4.5
    55Requires PHP: 5.3
    66Tested up to: 5.2
    7 Stable tag: 1.0.2
     7Stable tag: 1.0.3
    88
    99Secure your website with Wordfence Login Security, providing two-factor authentication, login and registration CAPTCHA, and XML-RPC protection.
     
    5959== Changelog ==
    6060
     61= 1.0.3 - July 16, 2019 =
     62* Improvement: Added additional information about reCAPTCHA to its setting control.
     63* Improvement: Added a constant that may be overridden to customize the expiration time of login verification email links.
     64* Improvement: reCAPTCHA keys are now tested on saving to prevent accidentally inputting a v2 key.
     65* Improvement: Added a setting to control the reCAPTCHA human/bot threshold.
     66* Improvement: Added an option to trigger removal of Login Security tables and data on deactivation.
     67* Improvement: Reworked the reCAPTCHA implementation to trigger the token check on login/registration form submission to avoid the token expiring.
     68* Fix: Widened the reCAPTCHA key fields to allow the full keys to be visible.
     69* Fix: Addressed an issue when outbound UDP connections are blocked where the NTP check could log an error.
     70* Fix: Added handling for reCAPTCHA's JavaScript failing to load, which previously blocked logging in.
     71* Fix: Fixed the functionality of the button to send 2FA grace period notifications.
     72* Fix: Fixed a missing icon for some help links when running in standalone mode.
     73
    6174= 1.0.2 - May 30, 2019 =
    6275* Initial release
  • wordfence-login-security/tags/1.0.3/views/options/option-captcha.php

    r2098090 r2124103  
    1818                <ul class="wfls-flex-vertical wfls-flex-align-left">
    1919                    <li>
    20                         <strong id="wfls-enable-auth-captcha-label"><?php _e('Enable reCAPTCHA on the login and user registration pages', 'wordfence-2fa'); ?></strong>
     20                        <strong id="wfls-enable-auth-captcha-label"><?php _e('Enable reCAPTCHA on the login and user registration pages', 'wordfence-ls'); ?></strong>
    2121                    </li>
    22                     <li class="wfls-option-subtitle"><?php _e('Note: This feature requires a free site key and secret for the <a href="https://www.google.com/recaptcha/intro/v3.html" target="_blank" rel="noopener noreferrer">Google reCAPTCHA v3 Service</a>.', 'wordfence-2fa'); ?></li>
     22                    <li class="wfls-option-subtitle"><?php printf(__('reCAPTCHA v3 does not make users solve puzzles or click a checkbox like previous versions. The only visible part is the reCAPTCHA logo. If a visitor\'s browser fails the CAPTCHA, Wordfence will send an email to the user\'s address with a link they can click to verify that they are a user of your site. You can read further details <a href="%s" target="_blank" rel="noopener noreferrer">in our documentation</a>.', 'wordfence-ls'), \WordfenceLS\Controller_Support::esc_supportURL(\WordfenceLS\Controller_Support::ITEM_MODULE_LOGIN_SECURITY_CAPTCHA)); ?></li>
    2323                </ul>
    2424            </li>
     
    3131                <table>
    3232                    <tr class="wfls-option wfls-option-text" data-original-value="<?php echo esc_attr($siteKeyValue); ?>" data-text-option="<?php echo esc_attr($siteKeyOptionName); ?>">
    33                         <th id="wfls-enable-captcha-site-key-label" class="wfls-padding-add-bottom"><?php _e('reCAPTCHA v3 Site Key', 'wordfence'); ?></th>
     33                        <th id="wfls-enable-captcha-site-key-label" class="wfls-padding-add-bottom"><?php _e('reCAPTCHA v3 Site Key', 'wordfence-ls'); ?></th>
    3434                        <td class="wfls-option-text wfls-padding-add-bottom"><input type="text" name="recaptchaSiteKey" id="input-recaptchaSiteKey" class="wfls-form-control" value="<?php echo esc_attr($siteKeyValue); ?>"<?php if (!$currentEnableValue) { echo ' disabled'; } ?>></td>
    3535                    </tr>
    3636                    <tr class="wfls-option wfls-option-text" data-original-value="<?php echo esc_attr($secretValue); ?>" data-text-option="<?php echo esc_attr($secretOptionName); ?>">
    37                         <th id="wfls-enable-captcha-secret-label"><?php _e('reCAPTCHA v3 Secret', 'wordfence'); ?></th>
     37                        <th id="wfls-enable-captcha-secret-label"><?php _e('reCAPTCHA v3 Secret', 'wordfence-ls'); ?></th>
    3838                        <td class="wfls-option-text"><input type="text" name="recaptchaSecret" id="input-recaptchaSecret" class="wfls-form-control" value="<?php echo esc_attr($secretValue); ?>"<?php if (!$currentEnableValue) { echo ' disabled'; } ?>></td>
    3939                    </tr>
    4040                </table>
     41            </li>
     42        </ul>
     43        <ul class="wfls-option wfls-padding-no-top">
     44            <li class="wfls-option-spacer"></li>
     45            <li class="wfls-option-title">
     46                <ul class="wfls-flex-vertical wfls-flex-align-left">
     47                    <li class="wfls-option-subtitle"><?php _e('Note: This feature requires a free site key and secret for the <a href="https://www.google.com/recaptcha/intro/v3.html" target="_blank" rel="noopener noreferrer">Google reCAPTCHA v3 Service</a>.', 'wordfence-ls'); ?></li>
     48                </ul>
    4149            </li>
    4250        </ul>
  • wordfence-login-security/tags/1.0.3/views/options/option-require-2fa.php

    r2098090 r2124103  
    5454                <span id="wfls-require-2fa-grace-period-label" class="wfls-padding-add-left wfls-padding-add-right"><?php _e('Grace period to require 2FA', 'wordfence'); ?> </span>
    5555                <input type="text" name="require2FAGracePeriod" id="input-require2FAGracePeriod" class="wfls-datetime wfls-form-control" placeholder="Enabled on..." data-value="<?php echo $currentGracePeriodDateValue; ?>" data-original-value="<?php echo $currentGracePeriodDateValue; ?>"<?php echo $currentGracePeriodEnabledValue ? '' : ' disabled'; ?>>
    56                 <div class="wfls-padding-add-left"><a href="#" id="wfls-send-grace-period-notification" class="wfls-btn wfls-btn-sm wfls-btn-default<?php echo (\WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_ADMIN) && \WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED) && \WordfenceLS\Controller_Time::time() < \WordfenceLS\Controller_Settings::shared()->get_int(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD)) ? '' : ' wfls-disabled'; ?>"><?php _e('Send Notification', 'wordfence-2fa'); ?></a></div>
    5756            </li>
     57        </ul>
     58    </li>
     59    <li>
     60        <ul class="wfls-option wfls-padding-no-top">
     61            <li class="wfls-option-spacer"></li>
     62            <li class="wfls-option-spacer"></li>
     63            <li><a href="#" id="wfls-send-grace-period-notification" class="wfls-btn wfls-btn-sm wfls-btn-default<?php echo (\WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_ADMIN) && \WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED) && \WordfenceLS\Controller_Time::time() < \WordfenceLS\Controller_Settings::shared()->get_int(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD)) ? '' : ' wfls-disabled'; ?>"><?php _e('Send Notification', 'wordfence-2fa'); ?></a></li>
    5864        </ul>
    5965    </li>
  • wordfence-login-security/tags/1.0.3/views/settings/options.php

    r2098090 r2124103  
    1010        </div>
    1111        <div class="wfls-block-header-action wfls-block-header-action-text wfls-nowrap wfls-padding-add-right-responsive">
    12             <a href="#" id="wfls-cancel-changes" class="wfls-btn wfls-btn-sm wfls-btn-default wfls-disabled"><?php _e('Cancel Changes', 'wordfence-2fa'); ?></a>&nbsp;&nbsp;<a href="#" id="wfls-save-changes" class="wfls-btn wfls-btn-sm wfls-btn-primary wfls-disabled"><?php _e('Save Changes', 'wordfence-2fa'); ?></a>
     12            <a href="#" id="wfls-cancel-changes" class="wfls-btn wfls-btn-sm wfls-btn-default wfls-disabled"><?php _e('Cancel<span class="wfls-visible-sm-inline"> Changes</span>', 'wordfence-2fa'); ?></a>&nbsp;&nbsp;<a href="#" id="wfls-save-changes" class="wfls-btn wfls-btn-sm wfls-btn-primary wfls-disabled"><?php _e('Save<span class="wfls-visible-sm-inline"> Changes</span>', 'wordfence-2fa'); ?></a>
    1313        </div>
    1414    </div>
     
    114114                ?>
    115115            </li>
     116            <li>
     117                <?php
     118                echo \WordfenceLS\Model_View::create('options/option-captcha-threshold', array(
     119                ))->render();
     120                ?>
     121            </li>
     122            <li>
     123                <?php
     124                echo \WordfenceLS\Model_View::create('options/option-toggled', array(
     125                    'optionName' => \WordfenceLS\Controller_Settings::OPTION_CAPTCHA_TEST_MODE,
     126                    'enabledValue' => '1',
     127                    'disabledValue' => '0',
     128                    'value' => \WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_CAPTCHA_TEST_MODE) ? '1': '0',
     129                    'title' => new \WordfenceLS\Text\Model_HTML('<strong>' . __('Run reCAPTCHA in test mode', 'wordfence-2fa') . '</strong>'),
     130                    'subtitle' => __('While in test mode, reCAPTCHA will score login and registration requests but not actually block them. The scores will be recorded and can be used to select a human/bot threshold value.', 'wordfence-2fa'),
     131                ))->render();
     132                ?>
     133            </li>
    116134            <?php if (!WORDFENCE_LS_FROM_CORE): ?>
    117135            <li>
     
    121139            </li>
    122140            <?php endif; ?>
     141            <li>
     142                <?php
     143                echo \WordfenceLS\Model_View::create('options/option-toggled', array(
     144                    'optionName' => \WordfenceLS\Controller_Settings::OPTION_DELETE_ON_DEACTIVATION,
     145                    'enabledValue' => '1',
     146                    'disabledValue' => '0',
     147                    'value' => \WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_DELETE_ON_DEACTIVATION) ? '1': '0',
     148                    'title' => new \WordfenceLS\Text\Model_HTML('<strong>' . __('Delete Login Security tables and data on deactivation', 'wordfence-2fa') . '</strong>'),
     149                    'subtitle' => __('If enabled, all settings and 2FA records will be deleted on deactivation. If later reactivated, all users that previously had 2FA active will need to set it up again.', 'wordfence-2fa'),
     150                ))->render();
     151                ?>
     152            </li>
    123153        </ul>
    124154    </div>
  • wordfence-login-security/tags/1.0.3/wordfence-login-security.php

    r2098090 r2124103  
    55Author: Wordfence
    66Author URI: http://www.wordfence.com/
    7 Version: 1.0.2
     7Version: 1.0.3
    88Network: true
    99*/
     
    3434    define('WORDFENCE_LS_FROM_CORE', ($wfCoreActive && isset($wfCoreLoading) && $wfCoreLoading));
    3535   
    36     define('WORDFENCE_LS_VERSION', '1.0.2');
    37     define('WORDFENCE_LS_BUILD_NUMBER', '1559237323');
     36    define('WORDFENCE_LS_VERSION', '1.0.3');
     37    define('WORDFENCE_LS_BUILD_NUMBER', '1563297209');
     38   
     39    if (!defined('WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES')) { define('WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES', 15); }
    3840   
    3941    if (!WORDFENCE_LS_FROM_CORE) {
  • wordfence-login-security/trunk/classes/controller/ajax.php

    r2098090 r2124103  
    7373                'required_parameters' => array('nonce', 'id'),
    7474            ),
     75            'reset_recaptcha_stats' => array(
     76                'handler' => array($this, '_ajax_reset_recaptcha_stats_callback'),
     77                'permissions' => array(Controller_Permissions::CAP_MANAGE_SETTINGS => __('You do not have permission to reset reCAPTCHA statistics.', 'wordfence-2fa')),
     78                'required_parameters' => array('nonce'),
     79            ),
    7580        );
    7681       
     
    400405        foreach ($admins as $a) {
    401406            /** @var \WP_User $a */
    402             if (!Controller_Users::shared()->has_2fa_active($a)) {
     407            if (Controller_Users::shared()->has_2fa_active($a)) {
    403408                continue;
    404409            }
     
    447452        Controller_Notices::shared()->remove_notice($_POST['id'], false, wp_get_current_user());
    448453    }
     454   
     455    public function _ajax_reset_recaptcha_stats_callback() {
     456        Controller_Settings::shared()->set_array(Controller_Settings::OPTION_CAPTCHA_STATS, array('counts' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 'avg' => 0));
     457        $response = array('success' => true);
     458        return die(json_encode($response));
     459    }
    449460}
  • wordfence-login-security/trunk/classes/controller/captcha.php

    r2098090 r2124103  
    112112     */
    113113    public function is_human($score) {
     114        if (Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_CAPTCHA_TEST_MODE)) {
     115            return true;
     116        }
     117       
    114118        $threshold = $this->threshold();
    115119        return ($score >= $threshold || abs($score - $threshold) < 0.0001);
  • wordfence-login-security/trunk/classes/controller/db.php

    r2098090 r2124103  
    5959    }
    6060   
     61    public function uninstall() {
     62        $tables = array(self::TABLE_2FA_SECRETS, self::TABLE_SETTINGS);
     63        foreach ($tables as $table) {
     64            global $wpdb;
     65            $wpdb->query('DROP TABLE IF EXISTS `' . self::network_table($table) . '`');
     66        }
     67    }
     68   
    6169    protected function _create_schema() {
    6270        $tables = array(
  • wordfence-login-security/trunk/classes/controller/settings.php

    r2098090 r2124103  
    1919    const OPTION_ALLOW_XML_RPC = 'allow-xml-rpc';
    2020    const OPTION_ENABLE_AUTH_CAPTCHA = 'enable-auth-captcha';
     21    const OPTION_CAPTCHA_TEST_MODE = 'recaptcha-test-mode';
    2122    const OPTION_RECAPTCHA_SITE_KEY = 'recaptcha-site-key';
    2223    const OPTION_RECAPTCHA_SECRET = 'recaptcha-secret';
    2324    const OPTION_RECAPTCHA_THRESHOLD = 'recaptcha-threshold';
     25    const OPTION_DELETE_ON_DEACTIVATION = 'delete-deactivation';
    2426   
    2527    //Internal
     
    3133    const OPTION_SHARED_SYMMETRIC_SECRET_KEY = 'shared-symmetric-secret';
    3234    const OPTION_DISMISSED_FRESH_INSTALL_MODAL = 'dismissed-fresh-install-modal';
     35    const OPTION_CAPTCHA_STATS = 'captcha-stats';
    3336   
    3437    protected $_settingsStorage;
     
    6770            self::OPTION_ALLOW_XML_RPC => array('value' => true, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
    6871            self::OPTION_ENABLE_AUTH_CAPTCHA => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
     72            self::OPTION_CAPTCHA_STATS => array('value' => '{"counts":[0,0,0,0,0,0,0,0,0,0,0],"avg":0}', 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
    6973            self::OPTION_RECAPTCHA_THRESHOLD => array('value' => 0.5, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
    7074            self::OPTION_LAST_SECRET_REFRESH => array('value' => 0, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
     75            self::OPTION_DELETE_ON_DEACTIVATION => array('value' => false, 'autoload' => Model_Settings::AUTOLOAD_YES, 'allowOverwrite' => false),
    7176        ));
    7277    }
     
    130135            case self::OPTION_ALLOW_XML_RPC:
    131136            case self::OPTION_ENABLE_AUTH_CAPTCHA:
     137            case self::OPTION_CAPTCHA_TEST_MODE:
    132138            case self::OPTION_DISMISSED_FRESH_INSTALL_MODAL:
     139            case self::OPTION_DELETE_ON_DEACTIVATION:
    133140                return true;
    134141               
     
    139146            //Array
    140147            case self::OPTION_GLOBAL_NOTICES:
     148            case self::OPTION_CAPTCHA_STATS:
    141149                return preg_match('/^\[.*\]$/', $value) || preg_match('/^\{.*\}$/', $value); //Only a rough JSON validation
    142150               
     
    166174            case self::OPTION_RECAPTCHA_THRESHOLD:
    167175                return is_numeric($value) && $value >= 0 && $value <= 1;
     176            case self::OPTION_RECAPTCHA_SITE_KEY:
     177                if (empty($value)) {
     178                    return true;
     179                }
     180               
     181                $response = wp_remote_get('https://www.google.com/recaptcha/api.js?render=' . urlencode($value));
     182               
     183                if (!is_wp_error($response)) {
     184                    $status = wp_remote_retrieve_response_code($response);
     185                    if ($status == 200) {
     186                        return true;
     187                    }
     188                   
     189                    $data = wp_remote_retrieve_body($response);
     190                    if (strpos($data, 'grecaptcha') === false) {
     191                        return __('Unable to validate the reCAPTCHA site key. Please check the key and try again.', 'wordfence-2fa');
     192                    }
     193                    return true;
     194                }
     195                return sprintf(__('An error was encountered while validating the reCAPTCHA site key: %s', 'wordfence-2fa'), $response->get_error_message());
    168196        }
    169197        return true;
     
    202230            case self::OPTION_ALLOW_XML_RPC:
    203231            case self::OPTION_ENABLE_AUTH_CAPTCHA:
     232            case self::OPTION_CAPTCHA_TEST_MODE:
    204233            case self::OPTION_DISMISSED_FRESH_INSTALL_MODAL:
     234            case self::OPTION_DELETE_ON_DEACTIVATION:
    205235                return $this->_truthy_to_bool($value);
    206236               
  • wordfence-login-security/trunk/classes/controller/time.php

    r2098090 r2124103  
    9696        foreach ($servers as $s) {
    9797            $socket = @fsockopen('udp://' . $s, 123, $err_no, $err_str, 1);
    98             stream_set_timeout($socket, 1);
    9998            if ($socket) {
     99                stream_set_timeout($socket, 1);
    100100                $remote_originate = microtime(true);
    101101                $secondsNTP = ((int) $remote_originate) + self::NTP_EPOCH_CONVERT;
  • wordfence-login-security/trunk/classes/controller/users.php

    r2098090 r2124103  
    312312   
    313313    /**
     314     * Records the reCAPTCHA score for later display.
     315     *
     316     * This is not atomic, which means this can miscount on hits that overlap, but the overhead of being atomic is not
     317     * worth it for our use.
     318     *
     319     * @param \WP_User $user|null
     320     * @param float $score
     321     */
     322    public function record_captcha_score($user, $score) {
     323        if (!Controller_CAPTCHA::shared()->enabled()) { return; }
     324        if ($this->has_2fa_active($user)) { return; } //2FA activated users do not retrieve a score
     325       
     326        if ($user) { update_user_meta($user->ID, 'wfls-last-captcha-score', $score); }
     327        $stats = Controller_Settings::shared()->get_array(Controller_Settings::OPTION_CAPTCHA_STATS);
     328        $int_score = min(max((int) ($score * 10), 0), 10);
     329        $count = array_sum($stats['counts']);
     330        $stats['counts'][$int_score]++;
     331        $stats['avg'] = ($stats['avg'] * $count + $int_score) / ($count + 1);
     332        Controller_Settings::shared()->set_array(Controller_Settings::OPTION_CAPTCHA_STATS, $stats);
     333    }
     334   
     335    /**
    314336     * Returns the active and inactive user counts.
    315337     *
  • wordfence-login-security/trunk/classes/controller/wordfencels.php

    r2098090 r2124103  
    125125            }
    126126        }
     127       
     128        if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_CAPTCHA_TEST_MODE) && Controller_CAPTCHA::shared()->enabled() && Controller_Permissions::shared()->can_manage_settings()) {
     129            if (is_multisite()) {
     130                add_action('network_admin_notices', array($this, '_recaptcha_test_notice'));
     131            }
     132            else {
     133                add_action('admin_notices', array($this, '_recaptcha_test_notice'));
     134            }
     135        }
    127136    }
    128137   
     
    135144    }
    136145   
     146    public function _recaptcha_test_notice() {
     147        echo '<div class="notice notice-warning"><p>' . sprintf(__('reCAPTCHA test mode is enabled. While enabled, login and registration requests will be checked for their score but will not be blocked if the score is below the minimum score. <a href="%s">Manage Settings</a>', 'wordfence-2fa'), esc_url(network_admin_url('admin.php?page=WFLS#top#settings'))) . '</p></div>';
     148    }
     149   
    137150    /**
    138151     * Installation/Uninstallation
     
    151164            }
    152165            delete_option($opt);
     166        }
     167       
     168        if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_DELETE_ON_DEACTIVATION)) {
     169            Controller_DB::shared()->uninstall();
    153170        }
    154171    }
     
    243260            }
    244261            wp_enqueue_script('wordfence-ls-admin', Model_Asset::js('admin.js'), array('jquery'), WORDFENCE_LS_VERSION);
     262            if (!WORDFENCE_LS_FROM_CORE) {
     263                wp_register_script('chart-js', Model_Asset::js('Chart.bundle.min.js'), array('jquery'), '2.4.0');
     264                wp_register_script('wordfence-select2-js', Model_Asset::js('wfselect2.min.js'), array('jquery'), WORDFENCE_LS_VERSION);
     265                wp_register_style('wordfence-select2-css', Model_Asset::css('wfselect2.min.css'), array(), WORDFENCE_LS_VERSION);
     266            }
     267            wp_enqueue_script('chart-js');
     268            wp_enqueue_script('wordfence-select2-js');
     269            wp_enqueue_style('wordfence-select2-css');
    245270            wp_enqueue_style('wordfence-ls-admin', Model_Asset::css('admin.css'), array(), WORDFENCE_LS_VERSION);
    246271            wp_enqueue_style('wordfence-ls-colorbox', Model_Asset::css('colorbox.css'), array(), WORDFENCE_LS_VERSION);
    247272            wp_enqueue_style('wordfence-ls-ionicons', Model_Asset::css('ionicons.css'), array(), WORDFENCE_LS_VERSION);
     273            if (!WORDFENCE_LS_FROM_CORE) { wp_enqueue_style('wordfence-ls-font-awesome', Model_Asset::css('font-awesome.css'), array(), WORDFENCE_LS_VERSION); }
    248274            wp_localize_script('wordfence-ls-admin', 'WFLSVars', array(
    249275                'ajaxurl' => admin_url('admin-ajax.php'),
     
    400426                }
    401427               
    402                 update_user_meta($user->ID, 'wfls-last-captcha-score', $score);
     428                Controller_Users::shared()->record_captcha_score($user, $score);
    403429               
    404430                if (isset($_REQUEST['wfls-email-verification']) && !empty($_REQUEST['wfls-email-verification']) && is_string($_REQUEST['wfls-email-verification'])) {
     
    422448                    $encrypted = Model_Symmetric::encrypt((string) $user->ID);
    423449                    if ($encrypted) {
    424                         $jwt = new Model_JWT(array('user' => $encrypted), Controller_Time::time() + 60 * 15 /* minutes */);
     450                        $jwt = new Model_JWT(array('user' => $encrypted), Controller_Time::time() + 60 * WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES);
    425451                        $view = new Model_View('email/login-verification', array(
    426452                            'siteName' => get_bloginfo('name', 'raw'),
     
    618644       
    619645        if ($requireCAPTCHA) {
     646            Controller_Users::shared()->record_captcha_score(null, $score);
     647           
    620648            if (!Controller_CAPTCHA::shared()->is_human($score)) { //Score is below the human threshold, block the user registration
    621649                $encryptedIP = Model_Symmetric::encrypt(Model_Request::current()->ip());
  • wordfence-login-security/trunk/classes/model/settings/db.php

    r2098090 r2124103  
    2626        if ($wpdb->query($wpdb->prepare("INSERT INTO `{$table}` (`name`, `value`, `autoload`) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`), `autoload` = VALUES(`autoload`)", $key, $value, $autoload)) !== false && $autoload != self::AUTOLOAD_NO) {
    2727            $this->_update_cached($key, $value);
     28            do_action('wfls_settings_set', $key, $value);
    2829        }
    2930    }
  • wordfence-login-security/trunk/readme.txt

    r2098090 r2124103  
    11=== Wordfence Login Security ===
    2 Contributors: wfryan 
     2Contributors: wfryan, wfmattr, mmaunder, wfmatt
    33Tags: security, login security, 2fa, two factor authentication, captcha, xml-rpc, mfa, 2 factor
    44Requires at least: 4.5
     
    5959== Changelog ==
    6060
     61= 1.0.3 - July 16, 2019 =
     62* Improvement: Added additional information about reCAPTCHA to its setting control.
     63* Improvement: Added a constant that may be overridden to customize the expiration time of login verification email links.
     64* Improvement: reCAPTCHA keys are now tested on saving to prevent accidentally inputting a v2 key.
     65* Improvement: Added a setting to control the reCAPTCHA human/bot threshold.
     66* Improvement: Added an option to trigger removal of Login Security tables and data on deactivation.
     67* Improvement: Reworked the reCAPTCHA implementation to trigger the token check on login/registration form submission to avoid the token expiring.
     68* Fix: Widened the reCAPTCHA key fields to allow the full keys to be visible.
     69* Fix: Addressed an issue when outbound UDP connections are blocked where the NTP check could log an error.
     70* Fix: Added handling for reCAPTCHA's JavaScript failing to load, which previously blocked logging in.
     71* Fix: Fixed the functionality of the button to send 2FA grace period notifications.
     72* Fix: Fixed a missing icon for some help links when running in standalone mode.
     73
    6174= 1.0.2 - May 30, 2019 =
    6275* Initial release
  • wordfence-login-security/trunk/views/options/option-captcha.php

    r2098090 r2124103  
    1818                <ul class="wfls-flex-vertical wfls-flex-align-left">
    1919                    <li>
    20                         <strong id="wfls-enable-auth-captcha-label"><?php _e('Enable reCAPTCHA on the login and user registration pages', 'wordfence-2fa'); ?></strong>
     20                        <strong id="wfls-enable-auth-captcha-label"><?php _e('Enable reCAPTCHA on the login and user registration pages', 'wordfence-ls'); ?></strong>
    2121                    </li>
    22                     <li class="wfls-option-subtitle"><?php _e('Note: This feature requires a free site key and secret for the <a href="https://www.google.com/recaptcha/intro/v3.html" target="_blank" rel="noopener noreferrer">Google reCAPTCHA v3 Service</a>.', 'wordfence-2fa'); ?></li>
     22                    <li class="wfls-option-subtitle"><?php printf(__('reCAPTCHA v3 does not make users solve puzzles or click a checkbox like previous versions. The only visible part is the reCAPTCHA logo. If a visitor\'s browser fails the CAPTCHA, Wordfence will send an email to the user\'s address with a link they can click to verify that they are a user of your site. You can read further details <a href="%s" target="_blank" rel="noopener noreferrer">in our documentation</a>.', 'wordfence-ls'), \WordfenceLS\Controller_Support::esc_supportURL(\WordfenceLS\Controller_Support::ITEM_MODULE_LOGIN_SECURITY_CAPTCHA)); ?></li>
    2323                </ul>
    2424            </li>
     
    3131                <table>
    3232                    <tr class="wfls-option wfls-option-text" data-original-value="<?php echo esc_attr($siteKeyValue); ?>" data-text-option="<?php echo esc_attr($siteKeyOptionName); ?>">
    33                         <th id="wfls-enable-captcha-site-key-label" class="wfls-padding-add-bottom"><?php _e('reCAPTCHA v3 Site Key', 'wordfence'); ?></th>
     33                        <th id="wfls-enable-captcha-site-key-label" class="wfls-padding-add-bottom"><?php _e('reCAPTCHA v3 Site Key', 'wordfence-ls'); ?></th>
    3434                        <td class="wfls-option-text wfls-padding-add-bottom"><input type="text" name="recaptchaSiteKey" id="input-recaptchaSiteKey" class="wfls-form-control" value="<?php echo esc_attr($siteKeyValue); ?>"<?php if (!$currentEnableValue) { echo ' disabled'; } ?>></td>
    3535                    </tr>
    3636                    <tr class="wfls-option wfls-option-text" data-original-value="<?php echo esc_attr($secretValue); ?>" data-text-option="<?php echo esc_attr($secretOptionName); ?>">
    37                         <th id="wfls-enable-captcha-secret-label"><?php _e('reCAPTCHA v3 Secret', 'wordfence'); ?></th>
     37                        <th id="wfls-enable-captcha-secret-label"><?php _e('reCAPTCHA v3 Secret', 'wordfence-ls'); ?></th>
    3838                        <td class="wfls-option-text"><input type="text" name="recaptchaSecret" id="input-recaptchaSecret" class="wfls-form-control" value="<?php echo esc_attr($secretValue); ?>"<?php if (!$currentEnableValue) { echo ' disabled'; } ?>></td>
    3939                    </tr>
    4040                </table>
     41            </li>
     42        </ul>
     43        <ul class="wfls-option wfls-padding-no-top">
     44            <li class="wfls-option-spacer"></li>
     45            <li class="wfls-option-title">
     46                <ul class="wfls-flex-vertical wfls-flex-align-left">
     47                    <li class="wfls-option-subtitle"><?php _e('Note: This feature requires a free site key and secret for the <a href="https://www.google.com/recaptcha/intro/v3.html" target="_blank" rel="noopener noreferrer">Google reCAPTCHA v3 Service</a>.', 'wordfence-ls'); ?></li>
     48                </ul>
    4149            </li>
    4250        </ul>
  • wordfence-login-security/trunk/views/options/option-require-2fa.php

    r2098090 r2124103  
    5454                <span id="wfls-require-2fa-grace-period-label" class="wfls-padding-add-left wfls-padding-add-right"><?php _e('Grace period to require 2FA', 'wordfence'); ?> </span>
    5555                <input type="text" name="require2FAGracePeriod" id="input-require2FAGracePeriod" class="wfls-datetime wfls-form-control" placeholder="Enabled on..." data-value="<?php echo $currentGracePeriodDateValue; ?>" data-original-value="<?php echo $currentGracePeriodDateValue; ?>"<?php echo $currentGracePeriodEnabledValue ? '' : ' disabled'; ?>>
    56                 <div class="wfls-padding-add-left"><a href="#" id="wfls-send-grace-period-notification" class="wfls-btn wfls-btn-sm wfls-btn-default<?php echo (\WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_ADMIN) && \WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED) && \WordfenceLS\Controller_Time::time() < \WordfenceLS\Controller_Settings::shared()->get_int(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD)) ? '' : ' wfls-disabled'; ?>"><?php _e('Send Notification', 'wordfence-2fa'); ?></a></div>
    5756            </li>
     57        </ul>
     58    </li>
     59    <li>
     60        <ul class="wfls-option wfls-padding-no-top">
     61            <li class="wfls-option-spacer"></li>
     62            <li class="wfls-option-spacer"></li>
     63            <li><a href="#" id="wfls-send-grace-period-notification" class="wfls-btn wfls-btn-sm wfls-btn-default<?php echo (\WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_ADMIN) && \WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD_ENABLED) && \WordfenceLS\Controller_Time::time() < \WordfenceLS\Controller_Settings::shared()->get_int(\WordfenceLS\Controller_Settings::OPTION_REQUIRE_2FA_GRACE_PERIOD)) ? '' : ' wfls-disabled'; ?>"><?php _e('Send Notification', 'wordfence-2fa'); ?></a></li>
    5864        </ul>
    5965    </li>
  • wordfence-login-security/trunk/views/settings/options.php

    r2098090 r2124103  
    1010        </div>
    1111        <div class="wfls-block-header-action wfls-block-header-action-text wfls-nowrap wfls-padding-add-right-responsive">
    12             <a href="#" id="wfls-cancel-changes" class="wfls-btn wfls-btn-sm wfls-btn-default wfls-disabled"><?php _e('Cancel Changes', 'wordfence-2fa'); ?></a>&nbsp;&nbsp;<a href="#" id="wfls-save-changes" class="wfls-btn wfls-btn-sm wfls-btn-primary wfls-disabled"><?php _e('Save Changes', 'wordfence-2fa'); ?></a>
     12            <a href="#" id="wfls-cancel-changes" class="wfls-btn wfls-btn-sm wfls-btn-default wfls-disabled"><?php _e('Cancel<span class="wfls-visible-sm-inline"> Changes</span>', 'wordfence-2fa'); ?></a>&nbsp;&nbsp;<a href="#" id="wfls-save-changes" class="wfls-btn wfls-btn-sm wfls-btn-primary wfls-disabled"><?php _e('Save<span class="wfls-visible-sm-inline"> Changes</span>', 'wordfence-2fa'); ?></a>
    1313        </div>
    1414    </div>
     
    114114                ?>
    115115            </li>
     116            <li>
     117                <?php
     118                echo \WordfenceLS\Model_View::create('options/option-captcha-threshold', array(
     119                ))->render();
     120                ?>
     121            </li>
     122            <li>
     123                <?php
     124                echo \WordfenceLS\Model_View::create('options/option-toggled', array(
     125                    'optionName' => \WordfenceLS\Controller_Settings::OPTION_CAPTCHA_TEST_MODE,
     126                    'enabledValue' => '1',
     127                    'disabledValue' => '0',
     128                    'value' => \WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_CAPTCHA_TEST_MODE) ? '1': '0',
     129                    'title' => new \WordfenceLS\Text\Model_HTML('<strong>' . __('Run reCAPTCHA in test mode', 'wordfence-2fa') . '</strong>'),
     130                    'subtitle' => __('While in test mode, reCAPTCHA will score login and registration requests but not actually block them. The scores will be recorded and can be used to select a human/bot threshold value.', 'wordfence-2fa'),
     131                ))->render();
     132                ?>
     133            </li>
    116134            <?php if (!WORDFENCE_LS_FROM_CORE): ?>
    117135            <li>
     
    121139            </li>
    122140            <?php endif; ?>
     141            <li>
     142                <?php
     143                echo \WordfenceLS\Model_View::create('options/option-toggled', array(
     144                    'optionName' => \WordfenceLS\Controller_Settings::OPTION_DELETE_ON_DEACTIVATION,
     145                    'enabledValue' => '1',
     146                    'disabledValue' => '0',
     147                    'value' => \WordfenceLS\Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_DELETE_ON_DEACTIVATION) ? '1': '0',
     148                    'title' => new \WordfenceLS\Text\Model_HTML('<strong>' . __('Delete Login Security tables and data on deactivation', 'wordfence-2fa') . '</strong>'),
     149                    'subtitle' => __('If enabled, all settings and 2FA records will be deleted on deactivation. If later reactivated, all users that previously had 2FA active will need to set it up again.', 'wordfence-2fa'),
     150                ))->render();
     151                ?>
     152            </li>
    123153        </ul>
    124154    </div>
  • wordfence-login-security/trunk/wordfence-login-security.php

    r2098090 r2124103  
    55Author: Wordfence
    66Author URI: http://www.wordfence.com/
    7 Version: 1.0.2
     7Version: 1.0.3
    88Network: true
    99*/
     
    3434    define('WORDFENCE_LS_FROM_CORE', ($wfCoreActive && isset($wfCoreLoading) && $wfCoreLoading));
    3535   
    36     define('WORDFENCE_LS_VERSION', '1.0.2');
    37     define('WORDFENCE_LS_BUILD_NUMBER', '1559237323');
     36    define('WORDFENCE_LS_VERSION', '1.0.3');
     37    define('WORDFENCE_LS_BUILD_NUMBER', '1563297209');
     38   
     39    if (!defined('WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES')) { define('WORDFENCE_LS_EMAIL_VALIDITY_DURATION_MINUTES', 15); }
    3840   
    3941    if (!WORDFENCE_LS_FROM_CORE) {
Note: See TracChangeset for help on using the changeset viewer.