Limit Login Attempts Reloaded - Version 2.6.1

Version Description

Plugin returns the 403 Forbidden header after the limit of login attempts via XMLRPC is reached.

Added support of IP ranges in white/black lists.

Lockouts now can be released selectively.

Fixed the issue with encoding of special symbols in email notifications.

Download this release

Release Info

Developer wpchefgadget
Plugin Icon 128x128 Limit Login Attempts Reloaded
Version 2.6.1
Comparing to
See all releases

Code changes from version 2.5.0 to 2.6.1

core/Helpers.php CHANGED
@@ -39,13 +39,15 @@ class LLA_Helpers {
39
  'username' => $user_name,
40
  'counter' => $info['counter'],
41
  'gateway' => ( isset( $info['gateway'] ) ) ? $info['gateway'] : '-',
 
42
  );
43
  } else { // For old plugin version
44
  $new_log[0] = array(
45
  'ip' => $ip,
46
  'username' => $user_name,
47
  'counter' => $info,
48
- 'gateway' => '-'
 
49
  );
50
  }
51
 
39
  'username' => $user_name,
40
  'counter' => $info['counter'],
41
  'gateway' => ( isset( $info['gateway'] ) ) ? $info['gateway'] : '-',
42
+ 'unlocked' => !empty( $info['unlocked'] ),
43
  );
44
  } else { // For old plugin version
45
  $new_log[0] = array(
46
  'ip' => $ip,
47
  'username' => $user_name,
48
  'counter' => $info,
49
+ 'gateway' => '-',
50
+ 'unlocked' => false,
51
  );
52
  }
53
 
core/LimitLoginAttempts.php CHANGED
@@ -66,7 +66,7 @@ class Limit_Login_Attempts
66
  add_filter( 'limit_login_blacklist_ip', array( $this, 'check_blacklist_ips' ), 10, 2 );
67
  add_filter( 'limit_login_blacklist_usernames', array( $this, 'check_blacklist_usernames' ), 10, 2 );
68
  }
69
-
70
  /**
71
  * Hook 'plugins_loaded'
72
  */
@@ -77,13 +77,13 @@ class Limit_Login_Attempts
77
 
78
  // Check if installed old plugin
79
  $this->check_original_installed();
80
-
81
  if ( is_multisite() )
82
  require_once ABSPATH.'wp-admin/includes/plugin.php';
83
-
84
  $this->network_mode = is_multisite() && is_plugin_active_for_network('limit-login-attempts-reloaded/limit-login-attempts-reloaded.php');
85
-
86
-
87
  if ( $this->network_mode )
88
  {
89
  $this->allow_local_options = get_site_option( 'limit_login_allow_local_options', false );
@@ -94,7 +94,7 @@ class Limit_Login_Attempts
94
  $this->allow_local_options = true;
95
  $this->use_local_options = true;
96
  }
97
-
98
 
99
  // Setup default plugin options
100
  //$this->sanitize_options();
@@ -105,10 +105,10 @@ class Limit_Login_Attempts
105
  add_filter( 'shake_error_codes', array( $this, 'failure_shake' ) );
106
  add_action( 'login_head', array( $this, 'add_error_message' ) );
107
  add_action( 'login_errors', array( $this, 'fixup_error_messages' ) );
108
-
109
  if ( $this->network_mode )
110
  add_action( 'network_admin_menu', array( $this, 'network_admin_menu' ) );
111
-
112
  if ( $this->allow_local_options )
113
  add_action( 'admin_menu', array( $this, 'admin_menu' ) );
114
 
@@ -125,10 +125,27 @@ class Limit_Login_Attempts
125
  */
126
  add_action( 'wp_authenticate', array( $this, 'track_credentials' ), 10, 2 );
127
  add_action( 'authenticate', array( $this, 'authenticate_filter' ), 5, 3 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  }
129
 
130
  public function check_whitelist_ips( $allow, $ip ) {
131
- return in_array( $ip, (array) $this->get_option( 'whitelist' ) );
132
  }
133
 
134
  public function check_whitelist_usernames( $allow, $username ) {
@@ -136,13 +153,44 @@ class Limit_Login_Attempts
136
  }
137
 
138
  public function check_blacklist_ips( $allow, $ip ) {
139
- return in_array( $ip, (array) $this->get_option( 'blacklist' ) );
140
  }
141
 
142
  public function check_blacklist_usernames( $allow, $username ) {
143
  return in_array( $username, (array) $this->get_option( 'blacklist_usernames' ) );
144
  }
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  /**
147
  * @param $error IXR_Error
148
  *
@@ -278,7 +326,7 @@ class Limit_Login_Attempts
278
  {
279
  add_submenu_page( 'settings.php', 'Limit Login Attempts', 'Limit Login Attempts', 'manage_options', $this->_options_page_slug, array( $this, 'options_page' ) );
280
  }
281
-
282
  public function admin_menu()
283
  {
284
  add_options_page( 'Limit Login Attempts', 'Limit Login Attempts', 'manage_options', $this->_options_page_slug, array( $this, 'options_page' ) );
@@ -293,7 +341,7 @@ class Limit_Login_Attempts
293
  {
294
  if ( is_network_admin() )
295
  return network_admin_url( 'settings.php?page=limit-login-attempts' );
296
-
297
  return menu_page_url( $this->_options_page_slug, false );
298
  }
299
 
@@ -304,43 +352,43 @@ class Limit_Login_Attempts
304
  *
305
  * @return null
306
  */
307
- public function get_option( $option_name, $local = null )
308
  {
309
  if ( is_null( $local ) )
310
  $local = $this->use_local_options;
311
-
312
  $option = 'limit_login_'.$option_name;
313
-
314
  $func = $local ? 'get_option' : 'get_site_option';
315
  $value = $func( $option, null );
316
-
317
  if ( is_null( $value ) && isset( $this->default_options[ $option_name ] ) )
318
  $value = $this->default_options[ $option_name ];
319
-
320
  return $value;
321
  }
322
-
323
  public function update_option( $option_name, $value, $local = null )
324
  {
325
  if ( is_null( $local ) )
326
  $local = $this->use_local_options;
327
-
328
  $option = 'limit_login_'.$option_name;
329
-
330
  $func = $local ? 'update_option' : 'update_site_option';
331
-
332
  return $func( $option, $value );
333
  }
334
-
335
  public function add_option( $option_name, $value, $local=null )
336
  {
337
  if ( is_null( $local ) )
338
  $local = $this->use_local_options;
339
-
340
  $option = 'limit_login_'.$option_name;
341
-
342
  $func = $local ? 'add_option' : 'add_site_option';
343
-
344
  return $func( $option, $value, '', 'no' );
345
  }
346
 
@@ -358,11 +406,11 @@ class Limit_Login_Attempts
358
  }
359
  if ( $this->get_option('notify_email_after') > $this->get_option( 'allowed_lockouts' ) )
360
  $this->update_option( 'notify_email_after', $this->get_option( 'allowed_lockouts' ) );
361
-
362
  $args = explode( ',', $this->get_option( 'lockout_notify' ) );
363
  $args_allowed = explode( ',', LLA_LOCKOUT_NOTIFY_ALLOWED );
364
  $new_args = array_intersect( $args, $args_allowed );
365
-
366
  $this->update_option( 'lockout_notify', implode( ',', $new_args ) );
367
 
368
  $ctype = $this->get_option( 'client_type' );
@@ -555,6 +603,7 @@ class Limit_Login_Attempts
555
  }
556
 
557
  $blogname = $this->use_local_options ? get_option( 'blogname' ) : get_site_option( 'site_name' );
 
558
 
559
  if ( $whitelisted ) {
560
  $subject = sprintf( __( "[%s] Failed login attempts from whitelisted IP"
@@ -581,9 +630,10 @@ class Limit_Login_Attempts
581
 
582
  $admin_email = $this->use_local_options ? get_option( 'admin_email' ) : get_site_option( 'admin_email' );
583
 
 
584
  @wp_mail( $admin_email, $subject, $message );
585
  }
586
-
587
  /**
588
  * Logging of lockout (if configured)
589
  *
@@ -604,30 +654,18 @@ class Limit_Login_Attempts
604
  $ip = $this->get_address();
605
 
606
  /* can be written much simpler, if you do not mind php warnings */
607
- if ( isset( $log[ $ip ] ) ) {
608
- if ( isset( $log[ $ip ][ $user_login ] ) ) {
609
-
610
- if ( is_array( $log[ $ip ][ $user_login ] ) ) { // For new plugin version
611
- $log[ $ip ][ $user_login ]['counter'] += 1;
612
- } else { // For old plugin version
613
- $temp_counter = $log[ $ip ][ $user_login ];
614
- $log[ $ip ][ $user_login ] = array(
615
- 'counter' => $temp_counter + 1
616
- );
617
- }
618
- } else {
619
- $log[ $ip ][ $user_login ] = array(
620
- 'counter' => 1
621
- );
622
- }
623
- } else {
624
- $log[ $ip ] = array(
625
- $user_login => array(
626
- 'counter' => 1
627
- )
628
  );
629
- }
630
 
 
631
  $log[ $ip ][ $user_login ]['date'] = time();
632
 
633
  if ( isset( $_POST['woocommerce-login-nonce'] ) ) {
@@ -1024,14 +1062,14 @@ class Limit_Login_Attempts
1024
  public function options_page() {
1025
  $this->use_local_options = !is_network_admin();
1026
  $this->cleanup();
1027
-
1028
  if( !empty( $_POST ) )
1029
  {
1030
  check_admin_referer( 'limit-login-attempts-options' );
1031
-
1032
  if ( is_network_admin() )
1033
  $this->update_option( 'allow_local_options', !empty($_POST['allow_local_options']) );
1034
-
1035
  elseif ( $this->network_mode )
1036
  $this->update_option( 'use_local_options', empty($_POST['use_global_options']) );
1037
 
@@ -1064,7 +1102,7 @@ class Limit_Login_Attempts
1064
  $this->update_option('valid_duration', (int)$_POST['valid_duration'] * 3600 );
1065
  $this->update_option('allowed_lockouts', (int)$_POST['allowed_lockouts'] );
1066
  $this->update_option('long_duration', (int)$_POST['long_duration'] * 3600 );
1067
- $this->update_option('notify_email_after', (int)$_POST['email_after'] * 3600 );
1068
 
1069
  $white_list_ips = ( !empty( $_POST['lla_whitelist_ips'] ) ) ? explode("\n", str_replace("\r", "", $_POST['lla_whitelist_ips'] ) ) : array();
1070
 
@@ -1098,7 +1136,7 @@ class Limit_Login_Attempts
1098
  }
1099
  }
1100
  $this->update_option('blacklist', $black_list_ips );
1101
-
1102
  $black_list_usernames = ( !empty( $_POST['lla_blacklist_usernames'] ) ) ? explode("\n", str_replace("\r", "", $_POST['lla_blacklist_usernames'] ) ) : array();
1103
 
1104
  if( !empty( $black_list_usernames ) ) {
@@ -1109,7 +1147,7 @@ class Limit_Login_Attempts
1109
  }
1110
  }
1111
  $this->update_option('blacklist_usernames', $black_list_usernames );
1112
-
1113
  $notify_methods = array();
1114
  if( isset( $_POST[ 'lockout_notify_log' ] ) ) {
1115
  $notify_methods[] = 'log';
@@ -1118,16 +1156,49 @@ class Limit_Login_Attempts
1118
  $notify_methods[] = 'email';
1119
  }
1120
  $this->update_option('lockout_notify', implode( ',', $notify_methods ) );
1121
-
1122
  $this->sanitize_options();
1123
-
1124
- $this->show_error( __( 'Options changed', 'limit-login-attempts-reloaded' ) );
1125
  }
1126
  }
1127
-
1128
  include_once( LLA_PLUGIN_DIR . '/views/options-page.php' );
1129
  }
1130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1131
  /**
1132
  * Show error message
1133
  *
66
  add_filter( 'limit_login_blacklist_ip', array( $this, 'check_blacklist_ips' ), 10, 2 );
67
  add_filter( 'limit_login_blacklist_usernames', array( $this, 'check_blacklist_usernames' ), 10, 2 );
68
  }
69
+
70
  /**
71
  * Hook 'plugins_loaded'
72
  */
77
 
78
  // Check if installed old plugin
79
  $this->check_original_installed();
80
+
81
  if ( is_multisite() )
82
  require_once ABSPATH.'wp-admin/includes/plugin.php';
83
+
84
  $this->network_mode = is_multisite() && is_plugin_active_for_network('limit-login-attempts-reloaded/limit-login-attempts-reloaded.php');
85
+
86
+
87
  if ( $this->network_mode )
88
  {
89
  $this->allow_local_options = get_site_option( 'limit_login_allow_local_options', false );
94
  $this->allow_local_options = true;
95
  $this->use_local_options = true;
96
  }
97
+
98
 
99
  // Setup default plugin options
100
  //$this->sanitize_options();
105
  add_filter( 'shake_error_codes', array( $this, 'failure_shake' ) );
106
  add_action( 'login_head', array( $this, 'add_error_message' ) );
107
  add_action( 'login_errors', array( $this, 'fixup_error_messages' ) );
108
+
109
  if ( $this->network_mode )
110
  add_action( 'network_admin_menu', array( $this, 'network_admin_menu' ) );
111
+
112
  if ( $this->allow_local_options )
113
  add_action( 'admin_menu', array( $this, 'admin_menu' ) );
114
 
125
  */
126
  add_action( 'wp_authenticate', array( $this, 'track_credentials' ), 10, 2 );
127
  add_action( 'authenticate', array( $this, 'authenticate_filter' ), 5, 3 );
128
+
129
+ if ( defined('XMLRPC_REQUEST') && XMLRPC_REQUEST )
130
+ add_action( 'init', array( $this, 'check_xmlrpc_lock' ) );
131
+
132
+ add_action('wp_ajax_limit-login-unlock', array( $this, 'ajax_unlock' ) );
133
+ }
134
+
135
+ public function check_xmlrpc_lock()
136
+ {
137
+ if ( is_user_logged_in() || $this->is_ip_whitelisted() )
138
+ return;
139
+
140
+ if ( $this->is_ip_blacklisted() || !$this->is_limit_login_ok() )
141
+ {
142
+ header('HTTP/1.0 403 Forbidden');
143
+ exit;
144
+ }
145
  }
146
 
147
  public function check_whitelist_ips( $allow, $ip ) {
148
+ return $this->ip_in_range( $ip, (array) $this->get_option( 'whitelist' ) );
149
  }
150
 
151
  public function check_whitelist_usernames( $allow, $username ) {
153
  }
154
 
155
  public function check_blacklist_ips( $allow, $ip ) {
156
+ return $this->ip_in_range( $ip, (array) $this->get_option( 'blacklist' ) );
157
  }
158
 
159
  public function check_blacklist_usernames( $allow, $username ) {
160
  return in_array( $username, (array) $this->get_option( 'blacklist_usernames' ) );
161
  }
162
 
163
+ public function ip_in_range( $ip, $list )
164
+ {
165
+ foreach ( $list as $range )
166
+ {
167
+ $range = array_map('trim', explode('-', $range) );
168
+ if ( count( $range ) == 1 )
169
+ {
170
+ if ( (string)$ip === (string)$range[0] )
171
+ return true;
172
+ }
173
+ else
174
+ {
175
+ $low = ip2long( $range[0] );
176
+ $high = ip2long( $range[1] );
177
+ $ip = ip2long( $ip );
178
+
179
+ if ( $low === false || $high === false || $ip === false )
180
+ continue;
181
+
182
+ $low = (float)sprintf("%u",$low);
183
+ $high = (float)sprintf("%u",$high);
184
+ $ip = (float)sprintf("%u",$ip);
185
+
186
+ if ( $ip >= $low && $ip <= $high )
187
+ return true;
188
+ }
189
+ }
190
+
191
+ return false;
192
+ }
193
+
194
  /**
195
  * @param $error IXR_Error
196
  *
326
  {
327
  add_submenu_page( 'settings.php', 'Limit Login Attempts', 'Limit Login Attempts', 'manage_options', $this->_options_page_slug, array( $this, 'options_page' ) );
328
  }
329
+
330
  public function admin_menu()
331
  {
332
  add_options_page( 'Limit Login Attempts', 'Limit Login Attempts', 'manage_options', $this->_options_page_slug, array( $this, 'options_page' ) );
341
  {
342
  if ( is_network_admin() )
343
  return network_admin_url( 'settings.php?page=limit-login-attempts' );
344
+
345
  return menu_page_url( $this->_options_page_slug, false );
346
  }
347
 
352
  *
353
  * @return null
354
  */
355
+ public function get_option( $option_name, $local = null )
356
  {
357
  if ( is_null( $local ) )
358
  $local = $this->use_local_options;
359
+
360
  $option = 'limit_login_'.$option_name;
361
+
362
  $func = $local ? 'get_option' : 'get_site_option';
363
  $value = $func( $option, null );
364
+
365
  if ( is_null( $value ) && isset( $this->default_options[ $option_name ] ) )
366
  $value = $this->default_options[ $option_name ];
367
+
368
  return $value;
369
  }
370
+
371
  public function update_option( $option_name, $value, $local = null )
372
  {
373
  if ( is_null( $local ) )
374
  $local = $this->use_local_options;
375
+
376
  $option = 'limit_login_'.$option_name;
377
+
378
  $func = $local ? 'update_option' : 'update_site_option';
379
+
380
  return $func( $option, $value );
381
  }
382
+
383
  public function add_option( $option_name, $value, $local=null )
384
  {
385
  if ( is_null( $local ) )
386
  $local = $this->use_local_options;
387
+
388
  $option = 'limit_login_'.$option_name;
389
+
390
  $func = $local ? 'add_option' : 'add_site_option';
391
+
392
  return $func( $option, $value, '', 'no' );
393
  }
394
 
406
  }
407
  if ( $this->get_option('notify_email_after') > $this->get_option( 'allowed_lockouts' ) )
408
  $this->update_option( 'notify_email_after', $this->get_option( 'allowed_lockouts' ) );
409
+
410
  $args = explode( ',', $this->get_option( 'lockout_notify' ) );
411
  $args_allowed = explode( ',', LLA_LOCKOUT_NOTIFY_ALLOWED );
412
  $new_args = array_intersect( $args, $args_allowed );
413
+
414
  $this->update_option( 'lockout_notify', implode( ',', $new_args ) );
415
 
416
  $ctype = $this->get_option( 'client_type' );
603
  }
604
 
605
  $blogname = $this->use_local_options ? get_option( 'blogname' ) : get_site_option( 'site_name' );
606
+ $blogname = htmlspecialchars_decode( $blogname, ENT_QUOTES );
607
 
608
  if ( $whitelisted ) {
609
  $subject = sprintf( __( "[%s] Failed login attempts from whitelisted IP"
630
 
631
  $admin_email = $this->use_local_options ? get_option( 'admin_email' ) : get_site_option( 'admin_email' );
632
 
633
+ //var_dump( $blogname, $subject ); exit;
634
  @wp_mail( $admin_email, $subject, $message );
635
  }
636
+
637
  /**
638
  * Logging of lockout (if configured)
639
  *
654
  $ip = $this->get_address();
655
 
656
  /* can be written much simpler, if you do not mind php warnings */
657
+ if ( !isset( $log[ $ip ] ) )
658
+ $log[ $ip ] = array();
659
+
660
+ if ( !isset( $log[ $ip ][ $user_login ] ) )
661
+ $log[ $ip ][ $user_login ] = array( 'counter' => 0 );
662
+
663
+ elseif ( !is_array( $log[ $ip ][ $user_login ] ) )
664
+ $log[ $ip ][ $user_login ] = array(
665
+ 'counter' => $log[ $ip ][ $user_login ],
 
 
 
 
 
 
 
 
 
 
 
 
666
  );
 
667
 
668
+ $log[ $ip ][ $user_login ]['counter']++;
669
  $log[ $ip ][ $user_login ]['date'] = time();
670
 
671
  if ( isset( $_POST['woocommerce-login-nonce'] ) ) {
1062
  public function options_page() {
1063
  $this->use_local_options = !is_network_admin();
1064
  $this->cleanup();
1065
+
1066
  if( !empty( $_POST ) )
1067
  {
1068
  check_admin_referer( 'limit-login-attempts-options' );
1069
+
1070
  if ( is_network_admin() )
1071
  $this->update_option( 'allow_local_options', !empty($_POST['allow_local_options']) );
1072
+
1073
  elseif ( $this->network_mode )
1074
  $this->update_option( 'use_local_options', empty($_POST['use_global_options']) );
1075
 
1102
  $this->update_option('valid_duration', (int)$_POST['valid_duration'] * 3600 );
1103
  $this->update_option('allowed_lockouts', (int)$_POST['allowed_lockouts'] );
1104
  $this->update_option('long_duration', (int)$_POST['long_duration'] * 3600 );
1105
+ $this->update_option('notify_email_after', (int)$_POST['email_after'] );
1106
 
1107
  $white_list_ips = ( !empty( $_POST['lla_whitelist_ips'] ) ) ? explode("\n", str_replace("\r", "", $_POST['lla_whitelist_ips'] ) ) : array();
1108
 
1136
  }
1137
  }
1138
  $this->update_option('blacklist', $black_list_ips );
1139
+
1140
  $black_list_usernames = ( !empty( $_POST['lla_blacklist_usernames'] ) ) ? explode("\n", str_replace("\r", "", $_POST['lla_blacklist_usernames'] ) ) : array();
1141
 
1142
  if( !empty( $black_list_usernames ) ) {
1147
  }
1148
  }
1149
  $this->update_option('blacklist_usernames', $black_list_usernames );
1150
+
1151
  $notify_methods = array();
1152
  if( isset( $_POST[ 'lockout_notify_log' ] ) ) {
1153
  $notify_methods[] = 'log';
1156
  $notify_methods[] = 'email';
1157
  }
1158
  $this->update_option('lockout_notify', implode( ',', $notify_methods ) );
1159
+
1160
  $this->sanitize_options();
1161
+
1162
+ $this->show_error( __( 'Options saved.', 'limit-login-attempts-reloaded' ) );
1163
  }
1164
  }
1165
+
1166
  include_once( LLA_PLUGIN_DIR . '/views/options-page.php' );
1167
  }
1168
 
1169
+ public function ajax_unlock()
1170
+ {
1171
+ check_ajax_referer('limit-login-unlock', 'sec');
1172
+ $ip = (string)@$_POST['ip'];
1173
+
1174
+ $lockouts = (array)$this->get_option('lockouts');
1175
+
1176
+ if ( isset( $lockouts[ $ip ] ) )
1177
+ {
1178
+ unset( $lockouts[ $ip ] );
1179
+ $this->update_option( 'lockouts', $lockouts );
1180
+ }
1181
+
1182
+ //save to log
1183
+ $user_login = @(string)$_POST['username'];
1184
+ $log = $this->get_option( 'logged' );
1185
+
1186
+ if ( @$log[ $ip ][ $user_login ] )
1187
+ {
1188
+ if ( !is_array( $log[ $ip ][ $user_login ] ) )
1189
+ $log[ $ip ][ $user_login ] = array(
1190
+ 'counter' => $log[ $ip ][ $user_login ],
1191
+ );
1192
+ $log[ $ip ][ $user_login ]['unlocked'] = true;
1193
+
1194
+ $this->update_option( 'logged', $log );
1195
+ }
1196
+
1197
+ header('Content-Type: application/json');
1198
+ echo 'true';
1199
+ exit;
1200
+ }
1201
+
1202
  /**
1203
  * Show error message
1204
  *
limit-login-attempts-reloaded.php CHANGED
@@ -4,7 +4,7 @@
4
  Description: Limit the rate of login attempts, including by way of cookies and for each IP address.
5
  Author: wpchefgadget
6
  Text Domain: limit-login-attempts-reloaded
7
- Version: 2.5.0
8
 
9
  Copyright 2008 - 2012 Johan Eenfeldt, 2016 - 2017 WPChef
10
 
4
  Description: Limit the rate of login attempts, including by way of cookies and for each IP address.
5
  Author: wpchefgadget
6
  Text Domain: limit-login-attempts-reloaded
7
+ Version: 2.6.1
8
 
9
  Copyright 2008 - 2012 Johan Eenfeldt, 2016 - 2017 WPChef
10
 
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: wpchefgadget
3
  Tags: login, security, authentication, Limit Login Attempts, Limit Login Attempts Reloaded, Limit Login Attempts Revamped, Limit Login Attempts Renovated, Limit Login Attempts Updated, Better Limit Login Attempts, Limit Login Attempts Renewed, Limit Login Attempts Upgraded
4
  Requires at least: 3.0
5
- Tested up to: 4.7.2
6
- Stable tag: 2.5.0
7
 
8
  Reloaded version of the original Limit Login Attempts plugin for Login Protection by a team of WordPress developers.
9
 
@@ -49,6 +49,15 @@ Based on the original code from Limit Login Attemps plugin by Johan Eenfeldt.
49
 
50
  == Changelog ==
51
 
 
 
 
 
 
 
 
 
 
52
  = 2.5.0 =
53
  * Added Multi-site Compatibility and additional MU settings. https://wordpress.org/support/topic/multisite-compatibility-47/
54
 
2
  Contributors: wpchefgadget
3
  Tags: login, security, authentication, Limit Login Attempts, Limit Login Attempts Reloaded, Limit Login Attempts Revamped, Limit Login Attempts Renovated, Limit Login Attempts Updated, Better Limit Login Attempts, Limit Login Attempts Renewed, Limit Login Attempts Upgraded
4
  Requires at least: 3.0
5
+ Tested up to: 4.8
6
+ Stable tag: 2.6.1
7
 
8
  Reloaded version of the original Limit Login Attempts plugin for Login Protection by a team of WordPress developers.
9
 
49
 
50
  == Changelog ==
51
 
52
+ = 2.6.1 =
53
+ Plugin returns the 403 Forbidden header after the limit of login attempts via XMLRPC is reached.
54
+
55
+ Added support of IP ranges in white/black lists.
56
+
57
+ Lockouts now can be released selectively.
58
+
59
+ Fixed the issue with encoding of special symbols in email notifications.
60
+
61
  = 2.5.0 =
62
  * Added Multi-site Compatibility and additional MU settings. https://wordpress.org/support/topic/multisite-compatibility-47/
63
 
views/options-page.php CHANGED
@@ -74,12 +74,12 @@ $black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_li
74
  $('.use_global_options').change( function(){
75
  var form = $(this).siblings('table');
76
  form.stop();
77
-
78
  if ( this.checked )
79
  first ? form.hide() : form.fadeOut();
80
  else
81
  first ? form.show() : form.fadeIn();
82
-
83
  first = false;
84
  }).change();
85
  });
@@ -89,7 +89,7 @@ $black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_li
89
  <tr>
90
  <th scope="row" valign="top"><?php echo __( 'Lockout', 'limit-login-attempts-reloaded' ); ?></th>
91
  <td>
92
-
93
  <input type="text" size="3" maxlength="4"
94
  value="<?php echo( $this->get_option( 'allowed_retries' ) ); ?>"
95
  name="allowed_retries"/> <?php echo __( 'allowed retries', 'limit-login-attempts-reloaded' ); ?>
@@ -127,7 +127,7 @@ $black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_li
127
  valign="top"><?php echo __( 'Whitelist', 'limit-login-attempts-reloaded' ); ?></th>
128
  <td>
129
  <div class="field-col">
130
- <p class="description"><?php _e( 'One IP per line', 'limit-login-attempts-reloaded' ); ?></p>
131
  <textarea name="lla_whitelist_ips" rows="10" cols="50"><?php echo $white_list_ips; ?></textarea>
132
  </div>
133
  <div class="field-col">
@@ -141,7 +141,7 @@ $black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_li
141
  valign="top"><?php echo __( 'Blacklist', 'limit-login-attempts-reloaded' ); ?></th>
142
  <td>
143
  <div class="field-col">
144
- <p class="description"><?php _e( 'One IP per line', 'limit-login-attempts-reloaded' ); ?></p>
145
  <textarea name="lla_blacklist_ips" rows="10" cols="50"><?php echo $black_list_ips; ?></textarea>
146
  </div>
147
  <div class="field-col">
@@ -152,7 +152,7 @@ $black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_li
152
  </tr>
153
  </table>
154
  <p class="submit">
155
- <input class="button button-primary" name="update_options" value="<?php echo __( 'Change Options', 'limit-login-attempts-reloaded' ); ?>"
156
  type="submit"/>
157
  </p>
158
  </form>
@@ -160,6 +160,8 @@ $black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_li
160
  $log = $this->get_option( 'logged' );
161
  $log = LLA_Helpers::sorted_log_by_date( $log );
162
 
 
 
163
  if( is_array( $log ) && ! empty( $log ) ) { ?>
164
  <h3><?php echo __( 'Lockout log', 'limit-login-attempts-reloaded' ); ?></h3>
165
  <form action="<?php echo $this->get_options_page_uri(); ?>" method="post">
@@ -178,6 +180,7 @@ $black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_li
178
  <th scope="col"><?php echo _x( "IP", "Internet address", 'limit-login-attempts-reloaded' ); ?></th>
179
  <th scope="col"><?php _e( 'Tried to log in as', 'limit-login-attempts-reloaded' ); ?></th>
180
  <th scope="col"><?php _e( 'Gateway', 'limit-login-attempts-reloaded' ); ?></th>
 
181
  </tr>
182
 
183
  <?php foreach ( $log as $date => $user_info ) : ?>
@@ -186,11 +189,47 @@ $black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_li
186
  <td class="limit-login-ip"><?php echo $user_info['ip']; ?></td>
187
  <td class="limit-login-max"><?php echo $user_info['username'] . ' (' . $user_info['counter'] .' lockouts)'; ?></td>
188
  <td class="limit-login-gateway"><?php echo $user_info['gateway']; ?></td>
 
 
 
 
 
 
189
  </tr>
190
  <?php endforeach; ?>
191
 
192
  </table>
193
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  <?php
195
  } /* if showing $log */
196
  ?>
74
  $('.use_global_options').change( function(){
75
  var form = $(this).siblings('table');
76
  form.stop();
77
+
78
  if ( this.checked )
79
  first ? form.hide() : form.fadeOut();
80
  else
81
  first ? form.show() : form.fadeIn();
82
+
83
  first = false;
84
  }).change();
85
  });
89
  <tr>
90
  <th scope="row" valign="top"><?php echo __( 'Lockout', 'limit-login-attempts-reloaded' ); ?></th>
91
  <td>
92
+
93
  <input type="text" size="3" maxlength="4"
94
  value="<?php echo( $this->get_option( 'allowed_retries' ) ); ?>"
95
  name="allowed_retries"/> <?php echo __( 'allowed retries', 'limit-login-attempts-reloaded' ); ?>
127
  valign="top"><?php echo __( 'Whitelist', 'limit-login-attempts-reloaded' ); ?></th>
128
  <td>
129
  <div class="field-col">
130
+ <p class="description"><?php _e( 'One IP or IP range (1.2.3.4-5.6.7.8) per line', 'limit-login-attempts-reloaded' ); ?></p>
131
  <textarea name="lla_whitelist_ips" rows="10" cols="50"><?php echo $white_list_ips; ?></textarea>
132
  </div>
133
  <div class="field-col">
141
  valign="top"><?php echo __( 'Blacklist', 'limit-login-attempts-reloaded' ); ?></th>
142
  <td>
143
  <div class="field-col">
144
+ <p class="description"><?php _e( 'One IP or IP range (1.2.3.4-5.6.7.8) per line', 'limit-login-attempts-reloaded' ); ?></p>
145
  <textarea name="lla_blacklist_ips" rows="10" cols="50"><?php echo $black_list_ips; ?></textarea>
146
  </div>
147
  <div class="field-col">
152
  </tr>
153
  </table>
154
  <p class="submit">
155
+ <input class="button button-primary" name="update_options" value="<?php echo __( 'Save Options', 'limit-login-attempts-reloaded' ); ?>"
156
  type="submit"/>
157
  </p>
158
  </form>
160
  $log = $this->get_option( 'logged' );
161
  $log = LLA_Helpers::sorted_log_by_date( $log );
162
 
163
+ $lockouts = (array)$this->get_option('lockouts');
164
+
165
  if( is_array( $log ) && ! empty( $log ) ) { ?>
166
  <h3><?php echo __( 'Lockout log', 'limit-login-attempts-reloaded' ); ?></h3>
167
  <form action="<?php echo $this->get_options_page_uri(); ?>" method="post">
180
  <th scope="col"><?php echo _x( "IP", "Internet address", 'limit-login-attempts-reloaded' ); ?></th>
181
  <th scope="col"><?php _e( 'Tried to log in as', 'limit-login-attempts-reloaded' ); ?></th>
182
  <th scope="col"><?php _e( 'Gateway', 'limit-login-attempts-reloaded' ); ?></th>
183
+ <th>
184
  </tr>
185
 
186
  <?php foreach ( $log as $date => $user_info ) : ?>
189
  <td class="limit-login-ip"><?php echo $user_info['ip']; ?></td>
190
  <td class="limit-login-max"><?php echo $user_info['username'] . ' (' . $user_info['counter'] .' lockouts)'; ?></td>
191
  <td class="limit-login-gateway"><?php echo $user_info['gateway']; ?></td>
192
+ <td>
193
+ <?php if ( !empty( $lockouts[ $user_info['ip'] ] ) && $lockouts[ $user_info['ip'] ] > time() ) : ?>
194
+ <a href="#" class="button limit-login-unlock" data-ip="<?=esc_attr($user_info['ip'])?>" data-username="<?=esc_attr($user_info['username'])?>">Unlock</a>
195
+ <?php elseif ( $user_info['unlocked'] ): ?>
196
+ Unlocked
197
+ <?php endif ?>
198
  </tr>
199
  <?php endforeach; ?>
200
 
201
  </table>
202
  </div>
203
+ <script>jQuery( function($) {
204
+ $('.limit-login-log .limit-login-unlock').click( function()
205
+ {
206
+ var btn = $(this);
207
+
208
+ if ( btn.hasClass('disabled') )
209
+ return false;
210
+ btn.addClass( 'disabled' );
211
+
212
+ $.post( ajaxurl, {
213
+ action: 'limit-login-unlock',
214
+ sec: '<?=wp_create_nonce('limit-login-unlock') ?>',
215
+ ip: btn.data('ip'),
216
+ username: btn.data('username')
217
+ } )
218
+ .done( function(data) {
219
+ if ( data === true )
220
+ btn.fadeOut( function(){ $(this).parent().text('Unlocked') });
221
+ else
222
+ fail();
223
+ }).fail( fail );
224
+
225
+ function fail() {
226
+ alert('Connection error');
227
+ btn.removeClass('disabled');
228
+ }
229
+
230
+ return false;
231
+ } );
232
+ } )</script>
233
  <?php
234
  } /* if showing $log */
235
  ?>