Version Description
- Usernames and IP addresses can be white-listed and black-listed now. https://wordpress.org/support/topic/banning-specific-usernames/ https://wordpress.org/support/topic/good-831/
- The lockouts log has been inversed. https://wordpress.org/support/topic/inverse-log/
Download this release
Release Info
Developer | wpchefgadget |
Plugin | Limit Login Attempts Reloaded |
Version | 2.4.0 |
Comparing to | |
See all releases |
Code changes from version 2.3.0 to 2.4.0
- assets/css/limit-login-attempts.css +6 -3
- assets/sass/limit-login-attempts.scss +14 -8
- core/Helpers.php +43 -0
- core/LimitLoginAttempts.php +1069 -1081
- limit-login-attempts-reloaded.php +1 -1
- readme.txt +9 -5
- views/options-page.php +87 -31
assets/css/limit-login-attempts.css
CHANGED
@@ -1,6 +1,9 @@
|
|
1 |
-
.limit-login-
|
|
|
|
|
|
|
2 |
background-color: #fff; }
|
3 |
-
.limit-login-log table th, .limit-login-log table td {
|
4 |
padding: 10px; }
|
5 |
-
.limit-login-log table tr:nth-child(even) {
|
6 |
background-color: rgba(0, 0, 0, 0.09); }
|
1 |
+
.limit-login-page-settings .field-col {
|
2 |
+
display: inline-block;
|
3 |
+
margin-right: 20px; }
|
4 |
+
.limit-login-page-settings .limit-login-log table {
|
5 |
background-color: #fff; }
|
6 |
+
.limit-login-page-settings .limit-login-log table th, .limit-login-page-settings .limit-login-log table td {
|
7 |
padding: 10px; }
|
8 |
+
.limit-login-page-settings .limit-login-log table tr:nth-child(even) {
|
9 |
background-color: rgba(0, 0, 0, 0.09); }
|
assets/sass/limit-login-attempts.scss
CHANGED
@@ -1,11 +1,17 @@
|
|
1 |
-
.limit-login-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
background-color:
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
}
|
10 |
}
|
11 |
}
|
1 |
+
.limit-login-page-settings {
|
2 |
+
.field-col {
|
3 |
+
display: inline-block;
|
4 |
+
margin-right: 20px;
|
5 |
+
}
|
6 |
+
.limit-login-log {
|
7 |
+
table {
|
8 |
+
background-color: #fff;
|
9 |
+
th, td {
|
10 |
+
padding: 10px;
|
11 |
+
}
|
12 |
+
tr:nth-child(even) {
|
13 |
+
background-color: rgba(#000, .09);
|
14 |
+
}
|
15 |
}
|
16 |
}
|
17 |
}
|
core/Helpers.php
CHANGED
@@ -15,4 +15,47 @@ class LLA_Helpers {
|
|
15 |
|
16 |
echo '<div id="message" class="updated fade"><p>' . $msg . '</p></div>';
|
17 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
}
|
15 |
|
16 |
echo '<div id="message" class="updated fade"><p>' . $msg . '</p></div>';
|
17 |
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @param $log
|
21 |
+
*
|
22 |
+
* @return array
|
23 |
+
*/
|
24 |
+
public static function sorted_log_by_date( $log ) {
|
25 |
+
$new_log = array();
|
26 |
+
|
27 |
+
if ( ! is_array( $log ) || empty( $log ) ) {
|
28 |
+
return $new_log;
|
29 |
+
}
|
30 |
+
|
31 |
+
foreach ( $log as $ip => $users ) {
|
32 |
+
|
33 |
+
if ( ! empty( $users ) ) {
|
34 |
+
foreach ( $users as $user_name => $info ) {
|
35 |
+
|
36 |
+
if ( is_array( $info ) ) { // For new plugin version
|
37 |
+
$new_log[ $info['date'] ] = array(
|
38 |
+
'ip' => $ip,
|
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 |
+
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
}
|
56 |
+
|
57 |
+
krsort( $new_log );
|
58 |
+
|
59 |
+
return $new_log;
|
60 |
+
}
|
61 |
}
|
core/LimitLoginAttempts.php
CHANGED
@@ -5,1086 +5,1074 @@
|
|
5 |
*/
|
6 |
class Limit_Login_Attempts {
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
945 |
-
|
946 |
-
|
947 |
-
|
948 |
-
|
949 |
-
|
950 |
-
|
951 |
-
|
952 |
-
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
|
1030 |
-
|
1031 |
-
|
1032 |
-
|
1033 |
-
|
1034 |
-
|
1035 |
-
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
-
|
1040 |
-
|
1041 |
-
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
1062 |
-
|
1063 |
-
|
1064 |
-
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
1074 |
-
|
1075 |
-
|
1076 |
-
|
1077 |
-
$this->cleanup();
|
1078 |
-
include_once( LLA_PLUGIN_DIR . '/views/options-page.php' );
|
1079 |
-
}
|
1080 |
-
|
1081 |
-
/**
|
1082 |
-
* Show error message
|
1083 |
-
*
|
1084 |
-
* @param $msg
|
1085 |
-
*/
|
1086 |
-
public function show_error( $msg ) {
|
1087 |
-
LLA_Helpers::show_error( $msg );
|
1088 |
-
}
|
1089 |
|
1090 |
}
|
5 |
*/
|
6 |
class Limit_Login_Attempts {
|
7 |
|
8 |
+
/**
|
9 |
+
* Main plugin options
|
10 |
+
* @var array
|
11 |
+
*/
|
12 |
+
public $_options = array(
|
13 |
+
/* Are we behind a proxy? */
|
14 |
+
'client_type' => LLA_DIRECT_ADDR,
|
15 |
+
|
16 |
+
/* Lock out after this many tries */
|
17 |
+
'allowed_retries' => 4,
|
18 |
+
|
19 |
+
/* Lock out for this many seconds */
|
20 |
+
'lockout_duration' => 1200, // 20 minutes
|
21 |
+
|
22 |
+
/* Long lock out after this many lockouts */
|
23 |
+
'allowed_lockouts' => 4,
|
24 |
+
|
25 |
+
/* Long lock out for this many seconds */
|
26 |
+
'long_duration' => 86400, // 24 hours,
|
27 |
+
|
28 |
+
/* Reset failed attempts after this many seconds */
|
29 |
+
'valid_duration' => 43200, // 12 hours
|
30 |
+
|
31 |
+
/* Also limit malformed/forged cookies? */
|
32 |
+
'cookies' => true,
|
33 |
+
|
34 |
+
/* Notify on lockout. Values: '', 'log', 'email', 'log,email' */
|
35 |
+
'lockout_notify' => 'log',
|
36 |
+
|
37 |
+
/* If notify by email, do so after this number of lockouts */
|
38 |
+
'notify_email_after' => 4,
|
39 |
+
|
40 |
+
'whitelist' => array(),
|
41 |
+
'whitelist_usernames' => array(),
|
42 |
+
'blacklist' => array(),
|
43 |
+
'blacklist_usernames' => array(),
|
44 |
+
);
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Admin options page slug
|
48 |
+
* @var string
|
49 |
+
*/
|
50 |
+
private $_options_page_slug = 'limit-login-attempts';
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Errors messages
|
54 |
+
*
|
55 |
+
* @var array
|
56 |
+
*/
|
57 |
+
public $_errors = array();
|
58 |
+
|
59 |
+
public function __construct() {
|
60 |
+
$this->hooks_init();
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Register wp hooks and filters
|
65 |
+
*/
|
66 |
+
public function hooks_init() {
|
67 |
+
add_action( 'plugins_loaded', array( $this, 'setup' ), 9999 );
|
68 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
|
69 |
+
add_filter( 'limit_login_whitelist_ip', array( $this, 'check_whitelist_ips' ), 10, 2 );
|
70 |
+
add_filter( 'limit_login_whitelist_usernames', array( $this, 'check_whitelist_usernames' ), 10, 2 );
|
71 |
+
add_filter( 'limit_login_blacklist_ip', array( $this, 'check_blacklist_ips' ), 10, 2 );
|
72 |
+
add_filter( 'limit_login_blacklist_usernames', array( $this, 'check_blacklist_usernames' ), 10, 2 );
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Hook 'plugins_loaded'
|
77 |
+
*/
|
78 |
+
public function setup() {
|
79 |
+
|
80 |
+
// Load languages files
|
81 |
+
load_plugin_textdomain( 'limit-login-attempts-reloaded', false, plugin_basename( dirname( __FILE__ ) ) . '/../languages' );
|
82 |
+
|
83 |
+
// Check if installed old plugin
|
84 |
+
$this->check_original_installed();
|
85 |
+
|
86 |
+
// Setup default plugin options
|
87 |
+
$this->setup_options();
|
88 |
+
|
89 |
+
add_action( 'wp_login_failed', array( $this, 'limit_login_failed' ) );
|
90 |
+
add_filter( 'wp_authenticate_user', array( $this, 'wp_authenticate_user' ), 99999, 2 );
|
91 |
+
|
92 |
+
add_filter( 'shake_error_codes', array( $this, 'failure_shake' ) );
|
93 |
+
add_action( 'login_head', array( $this, 'add_error_message' ) );
|
94 |
+
add_action( 'login_errors', array( $this, 'fixup_error_messages' ) );
|
95 |
+
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
|
96 |
+
|
97 |
+
// Add notices for XMLRPC request
|
98 |
+
add_filter( 'xmlrpc_login_error', array( $this, 'xmlrpc_error_messages' ) );
|
99 |
+
|
100 |
+
// Add notices to woocommerce login page
|
101 |
+
add_action( 'wp_head', array( $this, 'add_wc_notices' ) );
|
102 |
+
|
103 |
+
/*
|
104 |
+
* This action should really be changed to the 'authenticate' filter as
|
105 |
+
* it will probably be deprecated. That is however only available in
|
106 |
+
* later versions of WP.
|
107 |
+
*/
|
108 |
+
add_action( 'wp_authenticate', array( $this, 'track_credentials' ), 10, 2 );
|
109 |
+
add_action( 'authenticate', array( $this, 'authenticate_filter' ), 5, 3 );
|
110 |
+
}
|
111 |
+
|
112 |
+
public function check_whitelist_ips( $allow, $ip ) {
|
113 |
+
return in_array( $ip, (array) $this->get_option( 'whitelist' ) );
|
114 |
+
}
|
115 |
+
|
116 |
+
public function check_whitelist_usernames( $allow, $username ) {
|
117 |
+
return in_array( $username, (array) $this->get_option( 'whitelist_usernames' ) );
|
118 |
+
}
|
119 |
+
|
120 |
+
public function check_blacklist_ips( $allow, $ip ) {
|
121 |
+
return in_array( $ip, (array) $this->get_option( 'blacklist' ) );
|
122 |
+
}
|
123 |
+
|
124 |
+
public function check_blacklist_usernames( $allow, $username ) {
|
125 |
+
return in_array( $username, (array) $this->get_option( 'blacklist_usernames' ) );
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* @param $error IXR_Error
|
130 |
+
*
|
131 |
+
* @return IXR_Error
|
132 |
+
*/
|
133 |
+
public function xmlrpc_error_messages( $error ) {
|
134 |
+
|
135 |
+
if ( ! class_exists( 'IXR_Error' ) ) {
|
136 |
+
return $error;
|
137 |
+
}
|
138 |
+
|
139 |
+
if ( ! $this->is_limit_login_ok() ) {
|
140 |
+
return new IXR_Error( 403, $this->error_msg() );
|
141 |
+
}
|
142 |
+
|
143 |
+
$ip = $this->get_address();
|
144 |
+
$retries = get_option( 'limit_login_retries' );
|
145 |
+
$valid = get_option( 'limit_login_retries_valid' );
|
146 |
+
|
147 |
+
/* Should we show retries remaining? */
|
148 |
+
|
149 |
+
if ( ! is_array( $retries ) || ! is_array( $valid ) ) {
|
150 |
+
/* no retries at all */
|
151 |
+
return $error;
|
152 |
+
}
|
153 |
+
if ( ! isset( $retries[ $ip ] ) || ! isset( $valid[ $ip ] ) || time() > $valid[ $ip ] ) {
|
154 |
+
/* no: no valid retries */
|
155 |
+
return $error;
|
156 |
+
}
|
157 |
+
if ( ( $retries[ $ip ] % $this->get_option( 'allowed_retries' ) ) == 0 ) {
|
158 |
+
/* no: already been locked out for these retries */
|
159 |
+
return $error;
|
160 |
+
}
|
161 |
+
|
162 |
+
$remaining = max( ( $this->get_option( 'allowed_retries' ) - ( $retries[ $ip ] % $this->get_option( 'allowed_retries' ) ) ), 0 );
|
163 |
+
|
164 |
+
return new IXR_Error( 403, sprintf( _n( "<strong>%d</strong> attempt remaining.", "<strong>%d</strong> attempts remaining.", $remaining, 'limit-login-attempts-reloaded' ), $remaining ) );
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Errors on WooCommerce account page
|
169 |
+
*/
|
170 |
+
public function add_wc_notices() {
|
171 |
+
|
172 |
+
global $limit_login_just_lockedout, $limit_login_nonempty_credentials, $limit_login_my_error_shown;
|
173 |
+
|
174 |
+
if ( ! function_exists( 'is_account_page' ) || ! function_exists( 'wc_add_notice' ) ) {
|
175 |
+
return;
|
176 |
+
}
|
177 |
+
|
178 |
+
/*
|
179 |
+
* During lockout we do not want to show any other error messages (like
|
180 |
+
* unknown user or empty password).
|
181 |
+
*/
|
182 |
+
if ( empty( $_POST ) && ! $this->is_limit_login_ok() && ! $limit_login_just_lockedout ) {
|
183 |
+
if ( is_account_page() ) {
|
184 |
+
wc_add_notice( $this->error_msg(), 'error' );
|
185 |
+
}
|
186 |
+
}
|
187 |
+
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* @param $user
|
192 |
+
* @param $username
|
193 |
+
* @param $password
|
194 |
+
*
|
195 |
+
* @return WP_Error | WP_User
|
196 |
+
*/
|
197 |
+
public function authenticate_filter( $user, $username, $password ) {
|
198 |
+
|
199 |
+
if ( ! empty( $username ) && ! empty( $password ) ) {
|
200 |
+
|
201 |
+
$ip = $this->get_address();
|
202 |
+
|
203 |
+
// Check if username is blacklisted
|
204 |
+
if ( ! $this->is_username_whitelisted( $username ) && ! $this->is_ip_whitelisted( $ip ) &&
|
205 |
+
( $this->is_username_blacklisted( $username ) || $this->is_ip_blacklisted( $ip ) )
|
206 |
+
) {
|
207 |
+
|
208 |
+
remove_filter( 'login_errors', array( $this, 'fixup_error_messages' ) );
|
209 |
+
remove_filter( 'login_head', array( $this, 'add_error_message' ) );
|
210 |
+
remove_filter( 'wp_login_failed', array( $this, 'limit_login_failed' ) );
|
211 |
+
remove_filter( 'wp_authenticate_user', array( $this, 'wp_authenticate_user' ), 99999 );
|
212 |
+
remove_filter( 'login_head', array( $this, 'add_error_message' ) );
|
213 |
+
remove_filter( 'login_errors', array( $this, 'fixup_error_messages' ) );
|
214 |
+
|
215 |
+
remove_filter( 'authenticate', 'wp_authenticate_username_password', 20 );
|
216 |
+
remove_filter( 'authenticate', 'wp_authenticate_email_password', 20 );
|
217 |
+
|
218 |
+
$user = new WP_Error();
|
219 |
+
$user->add( 'username_blacklisted', "<strong>ERROR:</strong> Too many failed login attempts." );
|
220 |
+
|
221 |
+
} elseif ( $this->is_username_whitelisted( $username ) || $this->is_ip_whitelisted( $ip ) ) {
|
222 |
+
|
223 |
+
remove_filter( 'wp_login_failed', array( $this, 'limit_login_failed' ) );
|
224 |
+
remove_filter( 'wp_authenticate_user', array( $this, 'wp_authenticate_user' ), 99999 );
|
225 |
+
remove_filter( 'login_head', array( $this, 'add_error_message' ) );
|
226 |
+
remove_filter( 'login_errors', array( $this, 'fixup_error_messages' ) );
|
227 |
+
|
228 |
+
}
|
229 |
+
|
230 |
+
}
|
231 |
+
|
232 |
+
return $user;
|
233 |
+
}
|
234 |
+
|
235 |
+
/**
|
236 |
+
* Check if the original plugin is installed
|
237 |
+
*/
|
238 |
+
private function check_original_installed() {
|
239 |
+
|
240 |
+
if ( defined( 'LIMIT_LOGIN_DIRECT_ADDR' ) ) { // Original plugin is installed
|
241 |
+
|
242 |
+
if ( $active_plugins = get_option( 'active_plugins' ) ) {
|
243 |
+
$deactivate_this = array(
|
244 |
+
'limit-login-attempts-reloaded/limit-login-attempts-reloaded.php'
|
245 |
+
);
|
246 |
+
$active_plugins = array_diff( $active_plugins, $deactivate_this );
|
247 |
+
update_option( 'active_plugins', $active_plugins );
|
248 |
+
|
249 |
+
add_action( 'admin_notices', function () {
|
250 |
+
?>
|
251 |
+
<div class="notice notice-error is-dismissible">
|
252 |
+
<p><?php _e( 'Please deactivate the Limit Login Attempts first.', 'limit-login-attempts-reloaded' ); ?></p>
|
253 |
+
</div>
|
254 |
+
<?php
|
255 |
+
} );
|
256 |
+
}
|
257 |
+
}
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Enqueue js and css
|
262 |
+
*/
|
263 |
+
public function enqueue() {
|
264 |
+
wp_enqueue_style( 'lla-main', LLA_PLUGIN_URL . '/assets/css/limit-login-attempts.css' );
|
265 |
+
}
|
266 |
+
|
267 |
+
/**
|
268 |
+
* Add admin options page
|
269 |
+
*/
|
270 |
+
public function admin_menu() {
|
271 |
+
global $wp_version;
|
272 |
+
|
273 |
+
// Modern WP?
|
274 |
+
if ( version_compare( $wp_version, '3.0', '>=' ) ) {
|
275 |
+
add_options_page( 'Limit Login Attempts', 'Limit Login Attempts', 'manage_options', $this->_options_page_slug, array(
|
276 |
+
$this,
|
277 |
+
'options_page'
|
278 |
+
) );
|
279 |
+
|
280 |
+
return;
|
281 |
+
}
|
282 |
+
|
283 |
+
// Older WPMU?
|
284 |
+
if ( function_exists( "get_current_site" ) ) {
|
285 |
+
add_submenu_page( 'wpmu-admin.php', 'Limit Login Attempts', 'Limit Login Attempts', 9, $this->_options_page_slug, array(
|
286 |
+
$this,
|
287 |
+
'options_page'
|
288 |
+
) );
|
289 |
+
|
290 |
+
return;
|
291 |
+
}
|
292 |
+
|
293 |
+
// Older WP
|
294 |
+
add_options_page( 'Limit Login Attempts', 'Limit Login Attempts', 9, $this->_options_page_slug, array(
|
295 |
+
$this,
|
296 |
+
'options_page'
|
297 |
+
) );
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Get the correct options page URI
|
302 |
+
*
|
303 |
+
* @return mixed
|
304 |
+
*/
|
305 |
+
public function get_options_page_uri() {
|
306 |
+
return menu_page_url( $this->_options_page_slug, false );
|
307 |
+
}
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Get option by name
|
311 |
+
*
|
312 |
+
* @param $option_name
|
313 |
+
*
|
314 |
+
* @return null
|
315 |
+
*/
|
316 |
+
public function get_option( $option_name ) {
|
317 |
+
return ( isset( $this->_options[ $option_name ] ) ) ? $this->_options[ $option_name ] : null;
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Setup main options
|
322 |
+
*/
|
323 |
+
public function setup_options() {
|
324 |
+
$this->update_option_from_db( 'limit_login_client_type', 'client_type' );
|
325 |
+
$this->update_option_from_db( 'limit_login_allowed_retries', 'allowed_retries' );
|
326 |
+
$this->update_option_from_db( 'limit_login_lockout_duration', 'lockout_duration' );
|
327 |
+
$this->update_option_from_db( 'limit_login_valid_duration', 'valid_duration' );
|
328 |
+
$this->update_option_from_db( 'limit_login_cookies', 'cookies' );
|
329 |
+
$this->update_option_from_db( 'limit_login_lockout_notify', 'lockout_notify' );
|
330 |
+
$this->update_option_from_db( 'limit_login_allowed_lockouts', 'allowed_lockouts' );
|
331 |
+
$this->update_option_from_db( 'limit_login_long_duration', 'long_duration' );
|
332 |
+
$this->update_option_from_db( 'limit_login_notify_email_after', 'notify_email_after' );
|
333 |
+
$this->update_option_from_db( 'limit_login_whitelist', 'whitelist' );
|
334 |
+
$this->update_option_from_db( 'limit_login_whitelist_usernames', 'whitelist_usernames' );
|
335 |
+
$this->update_option_from_db( 'limit_login_blacklist', 'blacklist' );
|
336 |
+
$this->update_option_from_db( 'limit_login_blacklist_usernames', 'blacklist_usernames' );
|
337 |
+
|
338 |
+
$this->sanitize_variables();
|
339 |
+
}
|
340 |
+
|
341 |
+
public function sanitize_variables() {
|
342 |
+
|
343 |
+
$this->sanitize_simple_int( 'allowed_retries' );
|
344 |
+
$this->sanitize_simple_int( 'lockout_duration' );
|
345 |
+
$this->sanitize_simple_int( 'valid_duration' );
|
346 |
+
$this->sanitize_simple_int( 'allowed_lockouts' );
|
347 |
+
$this->sanitize_simple_int( 'long_duration' );
|
348 |
+
|
349 |
+
$this->_options['cookies'] = ! ! $this->get_option( 'cookies' );
|
350 |
+
|
351 |
+
$notify_email_after = max( 1, intval( $this->get_option( 'notify_email_after' ) ) );
|
352 |
+
$this->_options['notify_email_after'] = min( $this->get_option( 'allowed_lockouts' ), $notify_email_after );
|
353 |
+
|
354 |
+
$args = explode( ',', $this->get_option( 'lockout_notify' ) );
|
355 |
+
$args_allowed = explode( ',', LLA_LOCKOUT_NOTIFY_ALLOWED );
|
356 |
+
$new_args = array();
|
357 |
+
foreach ( $args as $a ) {
|
358 |
+
if ( in_array( $a, $args_allowed ) ) {
|
359 |
+
$new_args[] = $a;
|
360 |
+
}
|
361 |
+
}
|
362 |
+
$this->_options['lockout_notify'] = implode( ',', $new_args );
|
363 |
+
|
364 |
+
if ( $this->get_option( 'client_type' ) != LLA_DIRECT_ADDR && $this->get_option( 'client_type' ) != LLA_PROXY_ADDR ) {
|
365 |
+
$this->_options['client_type'] = LLA_DIRECT_ADDR;
|
366 |
+
}
|
367 |
+
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* Make sure the variables make sense -- simple integer
|
372 |
+
*
|
373 |
+
* @param $var_name
|
374 |
+
*/
|
375 |
+
public function sanitize_simple_int( $var_name ) {
|
376 |
+
$this->_options[ $var_name ] = max( 1, intval( $this->get_option( $var_name ) ) );
|
377 |
+
}
|
378 |
+
|
379 |
+
/**
|
380 |
+
* Update options in db from global variables
|
381 |
+
*/
|
382 |
+
public function update_options() {
|
383 |
+
update_option( 'limit_login_client_type', $this->get_option( 'client_type' ) );
|
384 |
+
update_option( 'limit_login_allowed_retries', $this->get_option( 'allowed_retries' ) );
|
385 |
+
update_option( 'limit_login_lockout_duration', $this->get_option( 'lockout_duration' ) );
|
386 |
+
update_option( 'limit_login_allowed_lockouts', $this->get_option( 'allowed_lockouts' ) );
|
387 |
+
update_option( 'limit_login_long_duration', $this->get_option( 'long_duration' ) );
|
388 |
+
update_option( 'limit_login_valid_duration', $this->get_option( 'valid_duration' ) );
|
389 |
+
update_option( 'limit_login_lockout_notify', $this->get_option( 'lockout_notify' ) );
|
390 |
+
update_option( 'limit_login_notify_email_after', $this->get_option( 'notify_email_after' ) );
|
391 |
+
update_option( 'limit_login_cookies', $this->get_option( 'cookies' ) ? '1' : '0' );
|
392 |
+
update_option( 'limit_login_whitelist', $this->get_option( 'whitelist' ) );
|
393 |
+
update_option( 'limit_login_whitelist_usernames', $this->get_option( 'whitelist_usernames' ) );
|
394 |
+
update_option( 'limit_login_blacklist', $this->get_option( 'blacklist' ) );
|
395 |
+
update_option( 'limit_login_blacklist_usernames', $this->get_option( 'blacklist_usernames' ) );
|
396 |
+
}
|
397 |
+
|
398 |
+
public function update_option_from_db( $option, $var_name ) {
|
399 |
+
if ( false !== ( $option_value = get_option( $option ) ) ) {
|
400 |
+
$this->_options[ $var_name ] = $option_value;
|
401 |
+
}
|
402 |
+
}
|
403 |
+
|
404 |
+
/**
|
405 |
+
* Check if it is ok to login
|
406 |
+
*
|
407 |
+
* @return bool
|
408 |
+
*/
|
409 |
+
public function is_limit_login_ok() {
|
410 |
+
|
411 |
+
$ip = $this->get_address();
|
412 |
+
|
413 |
+
/* Check external whitelist filter */
|
414 |
+
if ( $this->is_ip_whitelisted( $ip ) ) {
|
415 |
+
return true;
|
416 |
+
}
|
417 |
+
|
418 |
+
/* lockout active? */
|
419 |
+
$lockouts = get_option( 'limit_login_lockouts' );
|
420 |
+
|
421 |
+
return ( ! is_array( $lockouts ) || ! isset( $lockouts[ $ip ] ) || time() >= $lockouts[ $ip ] );
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* Action when login attempt failed
|
426 |
+
*
|
427 |
+
* Increase nr of retries (if necessary). Reset valid value. Setup
|
428 |
+
* lockout if nr of retries are above threshold. And more!
|
429 |
+
*
|
430 |
+
* A note on external whitelist: retries and statistics are still counted and
|
431 |
+
* notifications done as usual, but no lockout is done.
|
432 |
+
*
|
433 |
+
* @param $username
|
434 |
+
*/
|
435 |
+
public function limit_login_failed( $username ) {
|
436 |
+
|
437 |
+
$ip = $this->get_address();
|
438 |
+
|
439 |
+
/* if currently locked-out, do not add to retries */
|
440 |
+
$lockouts = get_option( 'limit_login_lockouts' );
|
441 |
+
|
442 |
+
if ( ! is_array( $lockouts ) ) {
|
443 |
+
$lockouts = array();
|
444 |
+
}
|
445 |
+
|
446 |
+
if ( isset( $lockouts[ $ip ] ) && time() < $lockouts[ $ip ] ) {
|
447 |
+
return;
|
448 |
+
}
|
449 |
+
|
450 |
+
/* Get the arrays with retries and retries-valid information */
|
451 |
+
$retries = get_option( 'limit_login_retries' );
|
452 |
+
$valid = get_option( 'limit_login_retries_valid' );
|
453 |
+
|
454 |
+
if ( ! is_array( $retries ) ) {
|
455 |
+
$retries = array();
|
456 |
+
add_option( 'limit_login_retries', $retries, '', 'no' );
|
457 |
+
}
|
458 |
+
|
459 |
+
if ( ! is_array( $valid ) ) {
|
460 |
+
$valid = array();
|
461 |
+
add_option( 'limit_login_retries_valid', $valid, '', 'no' );
|
462 |
+
}
|
463 |
+
|
464 |
+
/* Check validity and add one to retries */
|
465 |
+
if ( isset( $retries[ $ip ] ) && isset( $valid[ $ip ] ) && time() < $valid[ $ip ] ) {
|
466 |
+
$retries[ $ip ] ++;
|
467 |
+
} else {
|
468 |
+
$retries[ $ip ] = 1;
|
469 |
+
}
|
470 |
+
$valid[ $ip ] = time() + $this->get_option( 'valid_duration' );
|
471 |
+
|
472 |
+
/* lockout? */
|
473 |
+
if ( $retries[ $ip ] % $this->get_option( 'allowed_retries' ) != 0 ) {
|
474 |
+
/*
|
475 |
+
* Not lockout (yet!)
|
476 |
+
* Do housecleaning (which also saves retry/valid values).
|
477 |
+
*/
|
478 |
+
$this->cleanup( $retries, null, $valid );
|
479 |
+
|
480 |
+
return;
|
481 |
+
}
|
482 |
+
|
483 |
+
/* lockout! */
|
484 |
+
|
485 |
+
$whitelisted = $this->is_ip_whitelisted( $ip );
|
486 |
+
|
487 |
+
$retries_long = $this->get_option( 'allowed_retries' ) * $this->get_option( 'allowed_lockouts' );
|
488 |
+
|
489 |
+
/*
|
490 |
+
* Note that retries and statistics are still counted and notifications
|
491 |
+
* done as usual for whitelisted ips , but no lockout is done.
|
492 |
+
*/
|
493 |
+
if ( $whitelisted ) {
|
494 |
+
if ( $retries[ $ip ] >= $retries_long ) {
|
495 |
+
unset( $retries[ $ip ] );
|
496 |
+
unset( $valid[ $ip ] );
|
497 |
+
}
|
498 |
+
} else {
|
499 |
+
global $limit_login_just_lockedout;
|
500 |
+
$limit_login_just_lockedout = true;
|
501 |
+
|
502 |
+
/* setup lockout, reset retries as needed */
|
503 |
+
if ( $retries[ $ip ] >= $retries_long ) {
|
504 |
+
/* long lockout */
|
505 |
+
$lockouts[ $ip ] = time() + $this->get_option( 'long_duration' );
|
506 |
+
unset( $retries[ $ip ] );
|
507 |
+
unset( $valid[ $ip ] );
|
508 |
+
} else {
|
509 |
+
/* normal lockout */
|
510 |
+
$lockouts[ $ip ] = time() + $this->get_option( 'lockout_duration' );
|
511 |
+
}
|
512 |
+
}
|
513 |
+
|
514 |
+
/* do housecleaning and save values */
|
515 |
+
$this->cleanup( $retries, $lockouts, $valid );
|
516 |
+
|
517 |
+
/* do any notification */
|
518 |
+
$this->notify( $username );
|
519 |
+
|
520 |
+
/* increase statistics */
|
521 |
+
$total = get_option( 'limit_login_lockouts_total' );
|
522 |
+
if ( $total === false || ! is_numeric( $total ) ) {
|
523 |
+
add_option( 'limit_login_lockouts_total', 1, '', 'no' );
|
524 |
+
} else {
|
525 |
+
update_option( 'limit_login_lockouts_total', $total + 1 );
|
526 |
+
}
|
527 |
+
}
|
528 |
+
|
529 |
+
/**
|
530 |
+
* Handle notification in event of lockout
|
531 |
+
*
|
532 |
+
* @param $user
|
533 |
+
*/
|
534 |
+
public function notify( $user ) {
|
535 |
+
$args = explode( ',', $this->get_option( 'lockout_notify' ) );
|
536 |
+
|
537 |
+
if ( empty( $args ) ) {
|
538 |
+
return;
|
539 |
+
}
|
540 |
+
|
541 |
+
foreach ( $args as $mode ) {
|
542 |
+
switch ( trim( $mode ) ) {
|
543 |
+
case 'email':
|
544 |
+
$this->notify_email( $user );
|
545 |
+
break;
|
546 |
+
case 'log':
|
547 |
+
$this->notify_log( $user );
|
548 |
+
break;
|
549 |
+
}
|
550 |
+
}
|
551 |
+
}
|
552 |
+
|
553 |
+
/**
|
554 |
+
* Email notification of lockout to admin (if configured)
|
555 |
+
*
|
556 |
+
* @param $user
|
557 |
+
*/
|
558 |
+
public function notify_email( $user ) {
|
559 |
+
$ip = $this->get_address();
|
560 |
+
$whitelisted = $this->is_ip_whitelisted( $ip );
|
561 |
+
|
562 |
+
$retries = get_option( 'limit_login_retries' );
|
563 |
+
if ( ! is_array( $retries ) ) {
|
564 |
+
$retries = array();
|
565 |
+
}
|
566 |
+
|
567 |
+
/* check if we are at the right nr to do notification */
|
568 |
+
if ( isset( $retries[ $ip ] ) && ( ( $retries[ $ip ] / $this->get_option( 'allowed_retries' ) ) % $this->get_option( 'notify_email_after' ) ) != 0 ) {
|
569 |
+
return;
|
570 |
+
}
|
571 |
+
|
572 |
+
/* Format message. First current lockout duration */
|
573 |
+
if ( ! isset( $retries[ $ip ] ) ) {
|
574 |
+
/* longer lockout */
|
575 |
+
$count = $this->get_option( 'allowed_retries' )
|
576 |
+
* $this->get_option( 'allowed_lockouts' );
|
577 |
+
$lockouts = $this->get_option( 'allowed_lockouts' );
|
578 |
+
$time = round( $this->get_option( 'long_duration' ) / 3600 );
|
579 |
+
$when = sprintf( _n( '%d hour', '%d hours', $time, 'limit-login-attempts-reloaded' ), $time );
|
580 |
+
} else {
|
581 |
+
/* normal lockout */
|
582 |
+
$count = $retries[ $ip ];
|
583 |
+
$lockouts = floor( $count / $this->get_option( 'allowed_retries' ) );
|
584 |
+
$time = round( $this->get_option( 'lockout_duration' ) / 60 );
|
585 |
+
$when = sprintf( _n( '%d minute', '%d minutes', $time, 'limit-login-attempts-reloaded' ), $time );
|
586 |
+
}
|
587 |
+
|
588 |
+
$blogname = $this->is_multisite() ? get_site_option( 'site_name' ) : get_option( 'blogname' );
|
589 |
+
|
590 |
+
if ( $whitelisted ) {
|
591 |
+
$subject = sprintf( __( "[%s] Failed login attempts from whitelisted IP"
|
592 |
+
, 'limit-login-attempts-reloaded' )
|
593 |
+
, $blogname );
|
594 |
+
} else {
|
595 |
+
$subject = sprintf( __( "[%s] Too many failed login attempts"
|
596 |
+
, 'limit-login-attempts-reloaded' )
|
597 |
+
, $blogname );
|
598 |
+
}
|
599 |
+
|
600 |
+
$message = sprintf( __( "%d failed login attempts (%d lockout(s)) from IP: %s"
|
601 |
+
, 'limit-login-attempts-reloaded' ) . "\r\n\r\n"
|
602 |
+
, $count, $lockouts, $ip );
|
603 |
+
if ( $user != '' ) {
|
604 |
+
$message .= sprintf( __( "Last user attempted: %s", 'limit-login-attempts-reloaded' )
|
605 |
+
. "\r\n\r\n", $user );
|
606 |
+
}
|
607 |
+
if ( $whitelisted ) {
|
608 |
+
$message .= __( "IP was NOT blocked because of external whitelist.", 'limit-login-attempts-reloaded' );
|
609 |
+
} else {
|
610 |
+
$message .= sprintf( __( "IP was blocked for %s", 'limit-login-attempts-reloaded' ), $when );
|
611 |
+
}
|
612 |
+
|
613 |
+
$admin_email = $this->is_multisite() ? get_site_option( 'admin_email' ) : get_option( 'admin_email' );
|
614 |
+
|
615 |
+
@wp_mail( $admin_email, $subject, $message );
|
616 |
+
}
|
617 |
+
|
618 |
+
/**
|
619 |
+
* Is this WP Multisite?
|
620 |
+
*
|
621 |
+
* @return bool
|
622 |
+
*/
|
623 |
+
public function is_multisite() {
|
624 |
+
return function_exists( 'get_site_option' ) && function_exists( 'is_multisite' ) && is_multisite();
|
625 |
+
}
|
626 |
+
|
627 |
+
/**
|
628 |
+
* Logging of lockout (if configured)
|
629 |
+
*
|
630 |
+
* @param $user_login
|
631 |
+
*
|
632 |
+
* @internal param $user
|
633 |
+
*/
|
634 |
+
public function notify_log( $user_login ) {
|
635 |
+
|
636 |
+
if ( ! $user_login ) {
|
637 |
+
return;
|
638 |
+
}
|
639 |
+
|
640 |
+
$log = $option = get_option( 'limit_login_logged' );
|
641 |
+
if ( ! is_array( $log ) ) {
|
642 |
+
$log = array();
|
643 |
+
}
|
644 |
+
$ip = $this->get_address();
|
645 |
+
|
646 |
+
/* can be written much simpler, if you do not mind php warnings */
|
647 |
+
if ( isset( $log[ $ip ] ) ) {
|
648 |
+
if ( isset( $log[ $ip ][ $user_login ] ) ) {
|
649 |
+
|
650 |
+
if ( is_array( $log[ $ip ][ $user_login ] ) ) { // For new plugin version
|
651 |
+
$log[ $ip ][ $user_login ]['counter'] += 1;
|
652 |
+
} else { // For old plugin version
|
653 |
+
$temp_counter = $log[ $ip ][ $user_login ];
|
654 |
+
$log[ $ip ][ $user_login ] = array(
|
655 |
+
'counter' => $temp_counter + 1
|
656 |
+
);
|
657 |
+
}
|
658 |
+
} else {
|
659 |
+
$log[ $ip ][ $user_login ] = array(
|
660 |
+
'counter' => 1
|
661 |
+
);
|
662 |
+
}
|
663 |
+
} else {
|
664 |
+
$log[ $ip ] = array(
|
665 |
+
$user_login => array(
|
666 |
+
'counter' => 1
|
667 |
+
)
|
668 |
+
);
|
669 |
+
}
|
670 |
+
|
671 |
+
$log[ $ip ][ $user_login ]['date'] = time();
|
672 |
+
|
673 |
+
if ( isset( $_POST['woocommerce-login-nonce'] ) ) {
|
674 |
+
$gateway = 'WooCommerce';
|
675 |
+
} elseif ( isset( $GLOBALS['wp_xmlrpc_server'] ) && is_object( $GLOBALS['wp_xmlrpc_server'] ) ) {
|
676 |
+
$gateway = 'XMLRPC';
|
677 |
+
} else {
|
678 |
+
$gateway = 'WP Login';
|
679 |
+
}
|
680 |
+
|
681 |
+
$log[ $ip ][ $user_login ]['gateway'] = $gateway;
|
682 |
+
|
683 |
+
if ( $option === false ) {
|
684 |
+
add_option( 'limit_login_logged', $log, '', 'no' ); /* no autoload */
|
685 |
+
} else {
|
686 |
+
update_option( 'limit_login_logged', $log );
|
687 |
+
}
|
688 |
+
}
|
689 |
+
|
690 |
+
/**
|
691 |
+
* Check if IP is whitelisted.
|
692 |
+
*
|
693 |
+
* This function allow external ip whitelisting using a filter. Note that it can
|
694 |
+
* be called multiple times during the login process.
|
695 |
+
*
|
696 |
+
* Note that retries and statistics are still counted and notifications
|
697 |
+
* done as usual for whitelisted ips , but no lockout is done.
|
698 |
+
*
|
699 |
+
* Example:
|
700 |
+
* function my_ip_whitelist($allow, $ip) {
|
701 |
+
* return ($ip == 'my-ip') ? true : $allow;
|
702 |
+
* }
|
703 |
+
* add_filter('limit_login_whitelist_ip', 'my_ip_whitelist', 10, 2);
|
704 |
+
*
|
705 |
+
* @param null $ip
|
706 |
+
*
|
707 |
+
* @return bool
|
708 |
+
*/
|
709 |
+
public function is_ip_whitelisted( $ip = null ) {
|
710 |
+
|
711 |
+
if ( is_null( $ip ) ) {
|
712 |
+
$ip = $this->get_address();
|
713 |
+
}
|
714 |
+
|
715 |
+
$whitelisted = apply_filters( 'limit_login_whitelist_ip', false, $ip );
|
716 |
+
|
717 |
+
return ( $whitelisted === true );
|
718 |
+
}
|
719 |
+
|
720 |
+
public function is_username_whitelisted( $username ) {
|
721 |
+
|
722 |
+
if ( empty( $username ) ) {
|
723 |
+
return false;
|
724 |
+
}
|
725 |
+
|
726 |
+
$whitelisted = apply_filters( 'limit_login_whitelist_usernames', false, $username );
|
727 |
+
|
728 |
+
return ( $whitelisted === true );
|
729 |
+
}
|
730 |
+
|
731 |
+
public function is_ip_blacklisted( $ip = null ) {
|
732 |
+
|
733 |
+
if ( is_null( $ip ) ) {
|
734 |
+
$ip = $this->get_address();
|
735 |
+
}
|
736 |
+
|
737 |
+
$whitelisted = apply_filters( 'limit_login_blacklist_ip', false, $ip );
|
738 |
+
|
739 |
+
return ( $whitelisted === true );
|
740 |
+
}
|
741 |
+
|
742 |
+
public function is_username_blacklisted( $username ) {
|
743 |
+
|
744 |
+
if ( empty( $username ) ) {
|
745 |
+
return false;
|
746 |
+
}
|
747 |
+
|
748 |
+
$whitelisted = apply_filters( 'limit_login_blacklist_usernames', false, $username );
|
749 |
+
|
750 |
+
return ( $whitelisted === true );
|
751 |
+
}
|
752 |
+
|
753 |
+
/**
|
754 |
+
* Filter: allow login attempt? (called from wp_authenticate())
|
755 |
+
*
|
756 |
+
* @param $user WP_User
|
757 |
+
* @param $password
|
758 |
+
*
|
759 |
+
* @return \WP_Error
|
760 |
+
*/
|
761 |
+
public function wp_authenticate_user( $user, $password ) {
|
762 |
+
|
763 |
+
if ( is_wp_error( $user ) ||
|
764 |
+
$this->check_whitelist_ips( false, $this->get_address() ) ||
|
765 |
+
$this->check_whitelist_usernames( false, $user->user_login ) ||
|
766 |
+
$this->is_limit_login_ok()
|
767 |
+
) {
|
768 |
+
|
769 |
+
return $user;
|
770 |
+
}
|
771 |
+
|
772 |
+
$error = new WP_Error();
|
773 |
+
|
774 |
+
global $limit_login_my_error_shown;
|
775 |
+
$limit_login_my_error_shown = true;
|
776 |
+
|
777 |
+
if ( $this->is_username_blacklisted( $user->user_login ) || $this->is_ip_blacklisted( $this->get_address() ) ) {
|
778 |
+
$error->add( 'username_blacklisted', "<strong>ERROR:</strong> Too many failed login attempts." );
|
779 |
+
} else {
|
780 |
+
// This error should be the same as in "shake it" filter below
|
781 |
+
$error->add( 'too_many_retries', $this->error_msg() );
|
782 |
+
}
|
783 |
+
|
784 |
+
return $error;
|
785 |
+
}
|
786 |
+
|
787 |
+
/**
|
788 |
+
* Filter: add this failure to login page "Shake it!"
|
789 |
+
*
|
790 |
+
* @param $error_codes
|
791 |
+
*
|
792 |
+
* @return array
|
793 |
+
*/
|
794 |
+
public function failure_shake( $error_codes ) {
|
795 |
+
$error_codes[] = 'too_many_retries';
|
796 |
+
$error_codes[] = 'username_blacklisted';
|
797 |
+
|
798 |
+
return $error_codes;
|
799 |
+
}
|
800 |
+
|
801 |
+
/**
|
802 |
+
* Keep track of if user or password are empty, to filter errors correctly
|
803 |
+
*
|
804 |
+
* @param $user
|
805 |
+
* @param $password
|
806 |
+
*/
|
807 |
+
public function track_credentials( $user, $password ) {
|
808 |
+
global $limit_login_nonempty_credentials;
|
809 |
+
|
810 |
+
$limit_login_nonempty_credentials = ( ! empty( $user ) && ! empty( $password ) );
|
811 |
+
}
|
812 |
+
|
813 |
+
/**
|
814 |
+
* Should we show errors and messages on this page?
|
815 |
+
*
|
816 |
+
* @return bool
|
817 |
+
*/
|
818 |
+
public function login_show_msg() {
|
819 |
+
if ( isset( $_GET['key'] ) ) {
|
820 |
+
/* reset password */
|
821 |
+
return false;
|
822 |
+
}
|
823 |
+
|
824 |
+
$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : '';
|
825 |
+
|
826 |
+
return ( $action != 'lostpassword' && $action != 'retrievepassword'
|
827 |
+
&& $action != 'resetpass' && $action != 'rp'
|
828 |
+
&& $action != 'register' );
|
829 |
+
}
|
830 |
+
|
831 |
+
/**
|
832 |
+
* Construct informative error message
|
833 |
+
*
|
834 |
+
* @return string
|
835 |
+
*/
|
836 |
+
public function error_msg() {
|
837 |
+
$ip = $this->get_address();
|
838 |
+
$lockouts = get_option( 'limit_login_lockouts' );
|
839 |
+
|
840 |
+
$msg = __( '<strong>ERROR</strong>: Too many failed login attempts.', 'limit-login-attempts-reloaded' ) . ' ';
|
841 |
+
|
842 |
+
if ( ! is_array( $lockouts ) || ! isset( $lockouts[ $ip ] ) || time() >= $lockouts[ $ip ] ) {
|
843 |
+
/* Huh? No timeout active? */
|
844 |
+
$msg .= __( 'Please try again later.', 'limit-login-attempts-reloaded' );
|
845 |
+
|
846 |
+
return $msg;
|
847 |
+
}
|
848 |
+
|
849 |
+
$when = ceil( ( $lockouts[ $ip ] - time() ) / 60 );
|
850 |
+
if ( $when > 60 ) {
|
851 |
+
$when = ceil( $when / 60 );
|
852 |
+
$msg .= sprintf( _n( 'Please try again in %d hour.', 'Please try again in %d hours.', $when, 'limit-login-attempts-reloaded' ), $when );
|
853 |
+
} else {
|
854 |
+
$msg .= sprintf( _n( 'Please try again in %d minute.', 'Please try again in %d minutes.', $when, 'limit-login-attempts-reloaded' ), $when );
|
855 |
+
}
|
856 |
+
|
857 |
+
return $msg;
|
858 |
+
}
|
859 |
+
|
860 |
+
/**
|
861 |
+
* Add a message to login page when necessary
|
862 |
+
*/
|
863 |
+
public function add_error_message() {
|
864 |
+
global $error, $limit_login_my_error_shown;
|
865 |
+
|
866 |
+
if ( ! $this->login_show_msg() || $limit_login_my_error_shown ) {
|
867 |
+
return;
|
868 |
+
}
|
869 |
+
|
870 |
+
$msg = $this->get_message();
|
871 |
+
|
872 |
+
if ( $msg != '' ) {
|
873 |
+
$limit_login_my_error_shown = true;
|
874 |
+
$error .= $msg;
|
875 |
+
}
|
876 |
+
|
877 |
+
return;
|
878 |
+
}
|
879 |
+
|
880 |
+
/**
|
881 |
+
* Fix up the error message before showing it
|
882 |
+
*
|
883 |
+
* @param $content
|
884 |
+
*
|
885 |
+
* @return string
|
886 |
+
*/
|
887 |
+
public function fixup_error_messages( $content ) {
|
888 |
+
global $limit_login_just_lockedout, $limit_login_nonempty_credentials, $limit_login_my_error_shown;
|
889 |
+
|
890 |
+
if ( ! $this->login_show_msg() ) {
|
891 |
+
return $content;
|
892 |
+
}
|
893 |
+
|
894 |
+
/*
|
895 |
+
* During lockout we do not want to show any other error messages (like
|
896 |
+
* unknown user or empty password).
|
897 |
+
*/
|
898 |
+
if ( ! $this->is_limit_login_ok() && ! $limit_login_just_lockedout ) {
|
899 |
+
return $this->error_msg();
|
900 |
+
}
|
901 |
+
|
902 |
+
/*
|
903 |
+
* We want to filter the messages 'Invalid username' and
|
904 |
+
* 'Invalid password' as that is an information leak regarding user
|
905 |
+
* account names (prior to WP 2.9?).
|
906 |
+
*
|
907 |
+
* Also, if more than one error message, put an extra <br /> tag between
|
908 |
+
* them.
|
909 |
+
*/
|
910 |
+
$msgs = explode( "<br />\n", $content );
|
911 |
+
|
912 |
+
if ( strlen( end( $msgs ) ) == 0 ) {
|
913 |
+
/* remove last entry empty string */
|
914 |
+
array_pop( $msgs );
|
915 |
+
}
|
916 |
+
|
917 |
+
$count = count( $msgs );
|
918 |
+
$my_warn_count = $limit_login_my_error_shown ? 1 : 0;
|
919 |
+
|
920 |
+
if ( $limit_login_nonempty_credentials && $count > $my_warn_count ) {
|
921 |
+
/* Replace error message, including ours if necessary */
|
922 |
+
$content = __( '<strong>ERROR</strong>: Incorrect username or password.', 'limit-login-attempts-reloaded' ) . "<br />\n";
|
923 |
+
|
924 |
+
if ( $limit_login_my_error_shown || $this->get_message() ) {
|
925 |
+
$content .= "<br />\n" . $this->get_message() . "<br />\n";
|
926 |
+
}
|
927 |
+
|
928 |
+
return $content;
|
929 |
+
} elseif ( $count <= 1 ) {
|
930 |
+
return $content;
|
931 |
+
}
|
932 |
+
|
933 |
+
$new = '';
|
934 |
+
while ( $count -- > 0 ) {
|
935 |
+
$new .= array_shift( $msgs ) . "<br />\n";
|
936 |
+
if ( $count > 0 ) {
|
937 |
+
$new .= "<br />\n";
|
938 |
+
}
|
939 |
+
}
|
940 |
+
|
941 |
+
return $new;
|
942 |
+
}
|
943 |
+
|
944 |
+
public function fixup_error_messages_wc( \WP_Error $error ) {
|
945 |
+
$error->add( 1, __( 'WC Error' ) );
|
946 |
+
}
|
947 |
+
|
948 |
+
/**
|
949 |
+
* Return current (error) message to show, if any
|
950 |
+
*
|
951 |
+
* @return string
|
952 |
+
*/
|
953 |
+
public function get_message() {
|
954 |
+
/* Check external whitelist */
|
955 |
+
if ( $this->is_ip_whitelisted() ) {
|
956 |
+
return '';
|
957 |
+
}
|
958 |
+
|
959 |
+
/* Is lockout in effect? */
|
960 |
+
if ( ! $this->is_limit_login_ok() ) {
|
961 |
+
return $this->error_msg();
|
962 |
+
}
|
963 |
+
|
964 |
+
return $this->retries_remaining_msg();
|
965 |
+
}
|
966 |
+
|
967 |
+
/**
|
968 |
+
* Construct retries remaining message
|
969 |
+
*
|
970 |
+
* @return string
|
971 |
+
*/
|
972 |
+
public function retries_remaining_msg() {
|
973 |
+
$ip = $this->get_address();
|
974 |
+
$retries = get_option( 'limit_login_retries' );
|
975 |
+
$valid = get_option( 'limit_login_retries_valid' );
|
976 |
+
|
977 |
+
/* Should we show retries remaining? */
|
978 |
+
|
979 |
+
if ( ! is_array( $retries ) || ! is_array( $valid ) ) {
|
980 |
+
/* no retries at all */
|
981 |
+
return '';
|
982 |
+
}
|
983 |
+
if ( ! isset( $retries[ $ip ] ) || ! isset( $valid[ $ip ] ) || time() > $valid[ $ip ] ) {
|
984 |
+
/* no: no valid retries */
|
985 |
+
return '';
|
986 |
+
}
|
987 |
+
if ( ( $retries[ $ip ] % $this->get_option( 'allowed_retries' ) ) == 0 ) {
|
988 |
+
/* no: already been locked out for these retries */
|
989 |
+
return '';
|
990 |
+
}
|
991 |
+
|
992 |
+
$remaining = max( ( $this->get_option( 'allowed_retries' ) - ( $retries[ $ip ] % $this->get_option( 'allowed_retries' ) ) ), 0 );
|
993 |
+
|
994 |
+
return sprintf( _n( "<strong>%d</strong> attempt remaining.", "<strong>%d</strong> attempts remaining.", $remaining, 'limit-login-attempts-reloaded' ), $remaining );
|
995 |
+
}
|
996 |
+
|
997 |
+
/**
|
998 |
+
* Get correct remote address
|
999 |
+
*
|
1000 |
+
* @param string $type_name
|
1001 |
+
*
|
1002 |
+
* @return string
|
1003 |
+
*/
|
1004 |
+
public function get_address( $type_name = '' ) {
|
1005 |
+
|
1006 |
+
if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) && ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
|
1007 |
+
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
1008 |
+
} elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
|
1009 |
+
return $_SERVER['REMOTE_ADDR'];
|
1010 |
+
} else {
|
1011 |
+
return '';
|
1012 |
+
}
|
1013 |
+
}
|
1014 |
+
|
1015 |
+
/**
|
1016 |
+
* Clean up old lockouts and retries, and save supplied arrays
|
1017 |
+
*
|
1018 |
+
* @param null $retries
|
1019 |
+
* @param null $lockouts
|
1020 |
+
* @param null $valid
|
1021 |
+
*/
|
1022 |
+
public function cleanup( $retries = null, $lockouts = null, $valid = null ) {
|
1023 |
+
$now = time();
|
1024 |
+
$lockouts = ! is_null( $lockouts ) ? $lockouts : get_option( 'limit_login_lockouts' );
|
1025 |
+
|
1026 |
+
/* remove old lockouts */
|
1027 |
+
if ( is_array( $lockouts ) ) {
|
1028 |
+
foreach ( $lockouts as $ip => $lockout ) {
|
1029 |
+
if ( $lockout < $now ) {
|
1030 |
+
unset( $lockouts[ $ip ] );
|
1031 |
+
}
|
1032 |
+
}
|
1033 |
+
update_option( 'limit_login_lockouts', $lockouts );
|
1034 |
+
}
|
1035 |
+
|
1036 |
+
/* remove retries that are no longer valid */
|
1037 |
+
$valid = ! is_null( $valid ) ? $valid : get_option( 'limit_login_retries_valid' );
|
1038 |
+
$retries = ! is_null( $retries ) ? $retries : get_option( 'limit_login_retries' );
|
1039 |
+
if ( ! is_array( $valid ) || ! is_array( $retries ) ) {
|
1040 |
+
return;
|
1041 |
+
}
|
1042 |
+
|
1043 |
+
foreach ( $valid as $ip => $lockout ) {
|
1044 |
+
if ( $lockout < $now ) {
|
1045 |
+
unset( $valid[ $ip ] );
|
1046 |
+
unset( $retries[ $ip ] );
|
1047 |
+
}
|
1048 |
+
}
|
1049 |
+
|
1050 |
+
/* go through retries directly, if for some reason they've gone out of sync */
|
1051 |
+
foreach ( $retries as $ip => $retry ) {
|
1052 |
+
if ( ! isset( $valid[ $ip ] ) ) {
|
1053 |
+
unset( $retries[ $ip ] );
|
1054 |
+
}
|
1055 |
+
}
|
1056 |
+
|
1057 |
+
update_option( 'limit_login_retries', $retries );
|
1058 |
+
update_option( 'limit_login_retries_valid', $valid );
|
1059 |
+
}
|
1060 |
+
|
1061 |
+
/**
|
1062 |
+
* Render admin options page
|
1063 |
+
*/
|
1064 |
+
public function options_page() {
|
1065 |
+
$this->cleanup();
|
1066 |
+
include_once( LLA_PLUGIN_DIR . '/views/options-page.php' );
|
1067 |
+
}
|
1068 |
+
|
1069 |
+
/**
|
1070 |
+
* Show error message
|
1071 |
+
*
|
1072 |
+
* @param $msg
|
1073 |
+
*/
|
1074 |
+
public function show_error( $msg ) {
|
1075 |
+
LLA_Helpers::show_error( $msg );
|
1076 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1077 |
|
1078 |
}
|
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.
|
8 |
|
9 |
Copyright 2008 - 2012 Johan Eenfeldt
|
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.4.0
|
8 |
|
9 |
Copyright 2008 - 2012 Johan Eenfeldt
|
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: 2.8
|
5 |
-
Tested up to: 4.
|
6 |
-
Stable tag: 2.
|
7 |
|
8 |
Reloaded version of the original Limit Login Attempts plugin for Login Protection by a team of WordPress developers.
|
9 |
|
@@ -20,9 +20,9 @@ Features:
|
|
20 |
* Informs the user about the remaining retries or lockout time on the login page.
|
21 |
* Optional logging and optional email notification.
|
22 |
* Handles server behind the reverse proxy.
|
23 |
-
* It is possible to whitelist IPs
|
24 |
-
* XMLRPC gateway protection.
|
25 |
-
* Woocommerce login page protection.
|
26 |
|
27 |
= Upgrading from the old Limit Login Attempts plugin =
|
28 |
1. Go to the Plugins section in your site's backend.
|
@@ -48,6 +48,10 @@ Based on the original code from Limit Login Attemps plugin by Johan Eenfeldt.
|
|
48 |
|
49 |
== Changelog ==
|
50 |
|
|
|
|
|
|
|
|
|
51 |
= 2.3.0 =
|
52 |
* IP addresses can be white-listed now. https://wordpress.org/support/topic/legal-user/
|
53 |
* A "Gateway" column is added to the lockouts log. It shows what endpoint an attacker was blocked from. https://wordpress.org/support/topic/xmlrpc-7/
|
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: 2.8
|
5 |
+
Tested up to: 4.7
|
6 |
+
Stable tag: 2.4.0
|
7 |
|
8 |
Reloaded version of the original Limit Login Attempts plugin for Login Protection by a team of WordPress developers.
|
9 |
|
20 |
* Informs the user about the remaining retries or lockout time on the login page.
|
21 |
* Optional logging and optional email notification.
|
22 |
* Handles server behind the reverse proxy.
|
23 |
+
* It is possible to whitelist/blacklist IPs and Usernames.
|
24 |
+
* **XMLRPC** gateway protection.
|
25 |
+
* **Woocommerce** login page protection.
|
26 |
|
27 |
= Upgrading from the old Limit Login Attempts plugin =
|
28 |
1. Go to the Plugins section in your site's backend.
|
48 |
|
49 |
== Changelog ==
|
50 |
|
51 |
+
= 2.4.0 =
|
52 |
+
* Usernames and IP addresses can be white-listed and black-listed now. https://wordpress.org/support/topic/banning-specific-usernames/ https://wordpress.org/support/topic/good-831/
|
53 |
+
* The lockouts log has been inversed. https://wordpress.org/support/topic/inverse-log/
|
54 |
+
|
55 |
= 2.3.0 =
|
56 |
* IP addresses can be white-listed now. https://wordpress.org/support/topic/legal-user/
|
57 |
* A "Gateway" column is added to the lockouts log. It shows what endpoint an attacker was blocked from. https://wordpress.org/support/topic/xmlrpc-7/
|
views/options-page.php
CHANGED
@@ -44,17 +44,54 @@ if( isset( $_POST[ 'update_options' ] ) ) {
|
|
44 |
$this->_options[ 'long_duration' ] = $_POST[ 'long_duration' ] * 3600;
|
45 |
$this->_options[ 'notify_email_after' ] = $_POST[ 'email_after' ];
|
46 |
|
47 |
-
$
|
48 |
|
49 |
-
if( !empty( $
|
50 |
-
foreach( $
|
51 |
if( '' == $ip ) {
|
52 |
-
unset( $
|
53 |
}
|
54 |
}
|
55 |
}
|
56 |
|
57 |
-
$this->_options['whitelist'] = $
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
$notify_methods = array();
|
60 |
if( isset( $_POST[ 'lockout_notify_log' ] ) ) {
|
@@ -79,10 +116,19 @@ $v = explode( ',', $this->get_option( 'lockout_notify' ) );
|
|
79 |
$log_checked = in_array( 'log', $v ) ? ' checked ' : '';
|
80 |
$email_checked = in_array( 'email', $v ) ? ' checked ' : '';
|
81 |
|
82 |
-
$
|
83 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
?>
|
85 |
-
<div class="wrap">
|
86 |
<h2><?php echo __( 'Limit Login Attempts Settings', 'limit-login-attempts-reloaded' ); ?></h2>
|
87 |
<h3><?php echo __( 'Statistics', 'limit-login-attempts-reloaded' ); ?></h3>
|
88 |
<form action="<?php echo $this->get_options_page_uri(); ?>" method="post">
|
@@ -146,7 +192,7 @@ $white_list = ( is_array( $white_list ) && !empty( $white_list ) ) ? implode( "\
|
|
146 |
valign="top"><?php echo __( 'Notify on lockout', 'limit-login-attempts-reloaded' ); ?></th>
|
147 |
<td>
|
148 |
<input type="checkbox" name="lockout_notify_log" <?php echo $log_checked; ?>
|
149 |
-
value="log"/> <?php echo __( '
|
150 |
<input type="checkbox" name="lockout_notify_email" <?php echo $email_checked; ?>
|
151 |
value="email"/> <?php echo __( 'Email to admin after', 'limit-login-attempts-reloaded' ); ?>
|
152 |
<input type="text" size="3" maxlength="4"
|
@@ -156,10 +202,30 @@ $white_list = ( is_array( $white_list ) && !empty( $white_list ) ) ? implode( "\
|
|
156 |
</tr>
|
157 |
<tr>
|
158 |
<th scope="row"
|
159 |
-
valign="top"><?php echo __( 'Whitelist
|
160 |
<td>
|
161 |
-
<
|
162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
</td>
|
164 |
</tr>
|
165 |
</table>
|
@@ -170,7 +236,8 @@ $white_list = ( is_array( $white_list ) && !empty( $white_list ) ) ? implode( "\
|
|
170 |
</form>
|
171 |
<?php
|
172 |
$log = get_option( 'limit_login_logged' );
|
173 |
-
|
|
|
174 |
if( is_array( $log ) && ! empty( $log ) ) { ?>
|
175 |
<h3><?php echo __( 'Lockout log', 'limit-login-attempts-reloaded' ); ?></h3>
|
176 |
<form action="<?php echo $this->get_options_page_uri(); ?>" method="post">
|
@@ -191,24 +258,13 @@ $white_list = ( is_array( $white_list ) && !empty( $white_list ) ) ? implode( "\
|
|
191 |
<th scope="col"><?php _e( 'Gateway', 'limit-login-attempts-reloaded' ); ?></th>
|
192 |
</tr>
|
193 |
|
194 |
-
<?php foreach ( $log as $
|
195 |
-
|
196 |
-
<
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
<td class="limit-login-ip"><?php echo $ip; ?></td>
|
202 |
-
<td class="limit-login-max"><?php echo $user_name . ' (' . $info['counter'] .' lockouts)'; ?></td>
|
203 |
-
<td class="limit-login-gateway"><?php echo ( isset( $info['gateway'] ) && !empty( $info['gateway'] ) ) ? $info['gateway'] : '-'; ?></td>
|
204 |
-
<?php else : // For old plugin version ?>
|
205 |
-
<td class="limit-login-date"></td>
|
206 |
-
<td class="limit-login-ip"><?php echo $ip; ?></td>
|
207 |
-
<td class="limit-login-max"><?php echo $user_name . ' (' . $info .' lockouts)'; ?></td>
|
208 |
-
<td class="limit-login-gateway">-</td>
|
209 |
-
<?php endif; ?>
|
210 |
-
</tr>
|
211 |
-
<?php endforeach; ?>
|
212 |
<?php endforeach; ?>
|
213 |
|
214 |
</table>
|
44 |
$this->_options[ 'long_duration' ] = $_POST[ 'long_duration' ] * 3600;
|
45 |
$this->_options[ 'notify_email_after' ] = $_POST[ 'email_after' ];
|
46 |
|
47 |
+
$white_list_ips = ( !empty( $_POST['lla_whitelist_ips'] ) ) ? explode("\n", str_replace("\r", "", $_POST['lla_whitelist_ips'] ) ) : array();
|
48 |
|
49 |
+
if( !empty( $white_list_ips ) ) {
|
50 |
+
foreach( $white_list_ips as $key => $ip ) {
|
51 |
if( '' == $ip ) {
|
52 |
+
unset( $white_list_ips[ $key ] );
|
53 |
}
|
54 |
}
|
55 |
}
|
56 |
|
57 |
+
$this->_options['whitelist'] = $white_list_ips;
|
58 |
+
|
59 |
+
$white_list_usernames = ( !empty( $_POST['lla_whitelist_usernames'] ) ) ? explode("\n", str_replace("\r", "", $_POST['lla_whitelist_usernames'] ) ) : array();
|
60 |
+
|
61 |
+
if( !empty( $white_list_usernames ) ) {
|
62 |
+
foreach( $white_list_usernames as $key => $ip ) {
|
63 |
+
if( '' == $ip ) {
|
64 |
+
unset( $white_list_usernames[ $key ] );
|
65 |
+
}
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
$this->_options['whitelist_usernames'] = $white_list_usernames;
|
70 |
+
|
71 |
+
|
72 |
+
$black_list_ips = ( !empty( $_POST['lla_blacklist_ips'] ) ) ? explode("\n", str_replace("\r", "", $_POST['lla_blacklist_ips'] ) ) : array();
|
73 |
+
|
74 |
+
if( !empty( $black_list_ips ) ) {
|
75 |
+
foreach( $black_list_ips as $key => $ip ) {
|
76 |
+
if( '' == $ip ) {
|
77 |
+
unset( $black_list_ips[ $key ] );
|
78 |
+
}
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
$this->_options['blacklist'] = $black_list_ips;
|
83 |
+
|
84 |
+
$black_list_usernames = ( !empty( $_POST['lla_blacklist_usernames'] ) ) ? explode("\n", str_replace("\r", "", $_POST['lla_blacklist_usernames'] ) ) : array();
|
85 |
+
|
86 |
+
if( !empty( $black_list_usernames ) ) {
|
87 |
+
foreach( $black_list_usernames as $key => $ip ) {
|
88 |
+
if( '' == $ip ) {
|
89 |
+
unset( $black_list_usernames[ $key ] );
|
90 |
+
}
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
$this->_options['blacklist_usernames'] = $black_list_usernames;
|
95 |
|
96 |
$notify_methods = array();
|
97 |
if( isset( $_POST[ 'lockout_notify_log' ] ) ) {
|
116 |
$log_checked = in_array( 'log', $v ) ? ' checked ' : '';
|
117 |
$email_checked = in_array( 'email', $v ) ? ' checked ' : '';
|
118 |
|
119 |
+
$white_list_ips = $this->get_option( 'whitelist' );
|
120 |
+
$white_list_ips = ( is_array( $white_list_ips ) && !empty( $white_list_ips ) ) ? implode( "\n", $white_list_ips ) : '';
|
121 |
+
|
122 |
+
$white_list_usernames = $this->get_option( 'whitelist_usernames' );
|
123 |
+
$white_list_usernames = ( is_array( $white_list_usernames ) && !empty( $white_list_usernames ) ) ? implode( "\n", $white_list_usernames ) : '';
|
124 |
+
|
125 |
+
$black_list_ips = $this->get_option( 'blacklist' );
|
126 |
+
$black_list_ips = ( is_array( $black_list_ips ) && !empty( $black_list_ips ) ) ? implode( "\n", $black_list_ips ) : '';
|
127 |
+
|
128 |
+
$black_list_usernames = $this->get_option( 'blacklist_usernames' );
|
129 |
+
$black_list_usernames = ( is_array( $black_list_usernames ) && !empty( $black_list_usernames ) ) ? implode( "\n", $black_list_usernames ) : '';
|
130 |
?>
|
131 |
+
<div class="wrap limit-login-page-settings">
|
132 |
<h2><?php echo __( 'Limit Login Attempts Settings', 'limit-login-attempts-reloaded' ); ?></h2>
|
133 |
<h3><?php echo __( 'Statistics', 'limit-login-attempts-reloaded' ); ?></h3>
|
134 |
<form action="<?php echo $this->get_options_page_uri(); ?>" method="post">
|
192 |
valign="top"><?php echo __( 'Notify on lockout', 'limit-login-attempts-reloaded' ); ?></th>
|
193 |
<td>
|
194 |
<input type="checkbox" name="lockout_notify_log" <?php echo $log_checked; ?>
|
195 |
+
value="log"/> <?php echo __( 'Lockout log', 'limit-login-attempts-reloaded' ); ?><br/>
|
196 |
<input type="checkbox" name="lockout_notify_email" <?php echo $email_checked; ?>
|
197 |
value="email"/> <?php echo __( 'Email to admin after', 'limit-login-attempts-reloaded' ); ?>
|
198 |
<input type="text" size="3" maxlength="4"
|
202 |
</tr>
|
203 |
<tr>
|
204 |
<th scope="row"
|
205 |
+
valign="top"><?php echo __( 'Whitelist', 'limit-login-attempts-reloaded' ); ?></th>
|
206 |
<td>
|
207 |
+
<div class="field-col">
|
208 |
+
<p class="description"><?php _e( 'One IP per line', 'limit-login-attempts-reloaded' ); ?></p>
|
209 |
+
<textarea name="lla_whitelist_ips" rows="10" cols="50"><?php echo $white_list_ips; ?></textarea>
|
210 |
+
</div>
|
211 |
+
<div class="field-col">
|
212 |
+
<p class="description"><?php _e( 'One Username per line', 'limit-login-attempts-reloaded' ); ?></p>
|
213 |
+
<textarea name="lla_whitelist_usernames" rows="10" cols="50"><?php echo $white_list_usernames; ?></textarea>
|
214 |
+
</div>
|
215 |
+
</td>
|
216 |
+
</tr>
|
217 |
+
<tr>
|
218 |
+
<th scope="row"
|
219 |
+
valign="top"><?php echo __( 'Blacklist', 'limit-login-attempts-reloaded' ); ?></th>
|
220 |
+
<td>
|
221 |
+
<div class="field-col">
|
222 |
+
<p class="description"><?php _e( 'One IP per line', 'limit-login-attempts-reloaded' ); ?></p>
|
223 |
+
<textarea name="lla_blacklist_ips" rows="10" cols="50"><?php echo $black_list_ips; ?></textarea>
|
224 |
+
</div>
|
225 |
+
<div class="field-col">
|
226 |
+
<p class="description"><?php _e( 'One Username per line', 'limit-login-attempts-reloaded' ); ?></p>
|
227 |
+
<textarea name="lla_blacklist_usernames" rows="10" cols="50"><?php echo $black_list_usernames; ?></textarea>
|
228 |
+
</div>
|
229 |
</td>
|
230 |
</tr>
|
231 |
</table>
|
236 |
</form>
|
237 |
<?php
|
238 |
$log = get_option( 'limit_login_logged' );
|
239 |
+
$log = LLA_Helpers::sorted_log_by_date( $log );
|
240 |
+
|
241 |
if( is_array( $log ) && ! empty( $log ) ) { ?>
|
242 |
<h3><?php echo __( 'Lockout log', 'limit-login-attempts-reloaded' ); ?></h3>
|
243 |
<form action="<?php echo $this->get_options_page_uri(); ?>" method="post">
|
258 |
<th scope="col"><?php _e( 'Gateway', 'limit-login-attempts-reloaded' ); ?></th>
|
259 |
</tr>
|
260 |
|
261 |
+
<?php foreach ( $log as $date => $user_info ) : ?>
|
262 |
+
<tr>
|
263 |
+
<td class="limit-login-date"><?php echo date_i18n( 'F d, Y H:i', $date ); ?></td>
|
264 |
+
<td class="limit-login-ip"><?php echo $user_info['ip']; ?></td>
|
265 |
+
<td class="limit-login-max"><?php echo $user_info['username'] . ' (' . $user_info['counter'] .' lockouts)'; ?></td>
|
266 |
+
<td class="limit-login-gateway"><?php echo $user_info['gateway']; ?></td>
|
267 |
+
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
<?php endforeach; ?>
|
269 |
|
270 |
</table>
|