Version Description
- Add multisite network support.
- Keep unit tests from deleting settings. Note: removes the ability to run the unit tests without activating the plugin.
Download this release
Release Info
Developer | convissor |
Plugin | Login Security Solution |
Version | 0.4.0 |
Comparing to | |
See all releases |
Code changes from version 0.3.0 to 0.4.0
- admin.inc +123 -73
- languages/login-security-solution.pot +61 -63
- login-security-solution.php +49 -23
- readme.txt +27 -6
- tests/Accessor.php +0 -6
- tests/PasswordValidationTest.php +20 -15
- tests/TestCase.php +23 -2
admin.inc
CHANGED
@@ -6,7 +6,6 @@
|
|
6 |
*
|
7 |
* @package login-security-solution
|
8 |
* @link http://wordpress.org/extend/plugins/login-security-solution/
|
9 |
-
* @version 0.0.1
|
10 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2
|
11 |
* @author Daniel Convissor <danielc@analysisandsolutions.com>
|
12 |
* @copyright The Analysis and Solutions Company, 2012
|
@@ -18,18 +17,29 @@
|
|
18 |
*
|
19 |
* @package login-security-solution
|
20 |
* @link http://wordpress.org/extend/plugins/login-security-solution/
|
21 |
-
* @version 0.0.1
|
22 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2
|
23 |
* @author Daniel Convissor <danielc@analysisandsolutions.com>
|
24 |
* @copyright The Analysis and Solutions Company, 2012
|
25 |
*/
|
26 |
class login_security_solution_admin extends login_security_solution {
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
/**
|
28 |
* Metadata and labels for each element of the plugin's options
|
29 |
* @var array
|
30 |
*/
|
31 |
protected $fields;
|
32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
/**
|
34 |
* Key for the change password "don't remind me" checkbox
|
35 |
* @var string
|
@@ -48,6 +58,12 @@ class login_security_solution_admin extends login_security_solution {
|
|
48 |
*/
|
49 |
protected $option_pw_force_change_name;
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
/**
|
52 |
* Text for the plugin's password change page "don't remind me" button
|
53 |
* @var string
|
@@ -90,6 +106,16 @@ class login_security_solution_admin extends login_security_solution {
|
|
90 |
// Combine plugin's name with translation already in WP.
|
91 |
$this->text_settings = self::NAME . ' ' . __('Settings');
|
92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
// NON-STANDARD: This is for the password change page.
|
94 |
$this->option_pw_force_change_name = self::ID . '-pw-force-change-done';
|
95 |
$this->text_pw_force_change = __('Change All Passwords', self::ID);
|
@@ -108,6 +134,10 @@ class login_security_solution_admin extends login_security_solution {
|
|
108 |
public function activate() {
|
109 |
global $wpdb;
|
110 |
|
|
|
|
|
|
|
|
|
111 |
/*
|
112 |
* Create or alter the plugin's tables as needed.
|
113 |
*/
|
@@ -137,8 +167,14 @@ class login_security_solution_admin extends login_security_solution {
|
|
137 |
* Save this plugin's options to the database.
|
138 |
*/
|
139 |
|
|
|
|
|
|
|
140 |
update_option($this->option_name, $this->options);
|
141 |
add_option($this->option_pw_force_change_name, 0, '', 'no');
|
|
|
|
|
|
|
142 |
|
143 |
/*
|
144 |
* Store password hashes.
|
@@ -307,19 +343,18 @@ class login_security_solution_admin extends login_security_solution {
|
|
307 |
* plugin being displayed on WordPress' Plugins admin page.
|
308 |
*
|
309 |
* @param array $links the links generated thus far
|
310 |
-
* @param string $file the name of the plugin currently being rendered
|
311 |
* @return array
|
312 |
*/
|
313 |
-
public function plugin_action_links($links
|
314 |
-
|
315 |
-
|
316 |
-
|
|
|
|
|
|
|
|
|
|
|
317 |
|
318 |
-
// NON-STANDARD: This is for the password change page.
|
319 |
-
$links[] = '<a href="options-general.php?page='
|
320 |
-
. $this->option_pw_force_change_name . '">'
|
321 |
-
. $this->text_pw_force_change . '</a>' ;
|
322 |
-
}
|
323 |
return $links;
|
324 |
}
|
325 |
|
@@ -330,10 +365,11 @@ class login_security_solution_admin extends login_security_solution {
|
|
330 |
* any admin page is rendered
|
331 |
*/
|
332 |
public function admin_menu() {
|
333 |
-
|
|
|
334 |
$this->text_settings,
|
335 |
self::NAME,
|
336 |
-
|
337 |
self::ID,
|
338 |
array(&$this, 'page_settings')
|
339 |
);
|
@@ -390,7 +426,7 @@ class login_security_solution_admin extends login_security_solution {
|
|
390 |
*/
|
391 |
public function page_settings() {
|
392 |
echo '<h2>' . $this->hsc_utf8($this->text_settings) . '</h2>';
|
393 |
-
echo '<form action="
|
394 |
settings_fields($this->option_name);
|
395 |
do_settings_sections(self::ID);
|
396 |
submit_button();
|
@@ -410,19 +446,19 @@ class login_security_solution_admin extends login_security_solution {
|
|
410 |
*/
|
411 |
public function section_login() {
|
412 |
echo '<p>';
|
413 |
-
|
414 |
echo ' ';
|
415 |
-
|
416 |
echo ' ';
|
417 |
-
|
418 |
echo ' ';
|
419 |
-
|
420 |
echo ' ';
|
421 |
-
|
422 |
echo ' ';
|
423 |
-
|
424 |
echo ' ';
|
425 |
-
|
426 |
echo '</p>';
|
427 |
}
|
428 |
|
@@ -477,9 +513,9 @@ class login_security_solution_admin extends login_security_solution {
|
|
477 |
. $this->hsc_utf8($this->option_name)
|
478 |
. '[' . $this->hsc_utf8($name) . ']"'
|
479 |
. ' value="' . $this->hsc_utf8($this->options[$name]) . '" /> ';
|
480 |
-
echo $this->hsc_utf8($this->fields[$name]['text']
|
481 |
-
|
482 |
-
|
483 |
}
|
484 |
|
485 |
/**
|
@@ -512,9 +548,9 @@ class login_security_solution_admin extends login_security_solution {
|
|
512 |
|
513 |
if (!is_scalar($in[$name])) {
|
514 |
// Not translating this since only hackers will see it.
|
515 |
-
add_settings_error($this->option_name, $name,
|
516 |
-
|
517 |
-
|
518 |
continue;
|
519 |
}
|
520 |
|
@@ -522,28 +558,27 @@ class login_security_solution_admin extends login_security_solution {
|
|
522 |
case 'bool':
|
523 |
if ($in[$name] != 0 && $in[$name] != 1) {
|
524 |
// Not translating this since only hackers will see it.
|
525 |
-
add_settings_error($this->option_name, $name,
|
526 |
-
|
527 |
-
|
528 |
continue 2;
|
529 |
}
|
530 |
break;
|
531 |
case 'int':
|
532 |
if (!ctype_digit($in[$name])) {
|
533 |
-
add_settings_error($this->option_name, $name,
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
continue 2;
|
538 |
}
|
539 |
if (array_key_exists('greater_than', $field)
|
540 |
&& $in[$name] < $field['greater_than'])
|
541 |
{
|
542 |
-
add_settings_error($this->option_name, $name,
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
. ' ' . $default);
|
547 |
continue 2;
|
548 |
}
|
549 |
break;
|
@@ -554,11 +589,10 @@ class login_security_solution_admin extends login_security_solution {
|
|
554 |
// Special check to make sure Delay Tier 3 > Delay Tier 2.
|
555 |
$name = 'login_fail_tier_3';
|
556 |
if ($out[$name] <= $out['login_fail_tier_2']) {
|
557 |
-
add_settings_error($this->option_name, $name,
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
. ' ' . $default);
|
562 |
|
563 |
$out[$name] = $out['login_fail_tier_2'] + 5;
|
564 |
}
|
@@ -566,10 +600,10 @@ class login_security_solution_admin extends login_security_solution {
|
|
566 |
// Speical check to ensure reuse count is set if aging is enabled.
|
567 |
$name = 'pw_reuse_count';
|
568 |
if ($out['pw_change_days'] && !$out[$name]) {
|
569 |
-
add_settings_error($this->option_name, $name,
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
|
574 |
$out[$name] = 5;
|
575 |
}
|
@@ -588,10 +622,11 @@ class login_security_solution_admin extends login_security_solution {
|
|
588 |
* any admin page is rendered
|
589 |
*/
|
590 |
public function admin_menu_pw_force_change() {
|
591 |
-
|
|
|
592 |
$this->text_pw_force_change,
|
593 |
'',
|
594 |
-
|
595 |
$this->option_pw_force_change_name,
|
596 |
array(&$this, 'page_pw_force_change')
|
597 |
);
|
@@ -633,18 +668,18 @@ class login_security_solution_admin extends login_security_solution {
|
|
633 |
echo '<h2>' . $this->hsc_utf8($this->text_pw_force_change) . '</h2>';
|
634 |
|
635 |
echo '<p>';
|
636 |
-
|
637 |
echo ' ';
|
638 |
-
|
639 |
echo '</p>';
|
640 |
|
641 |
echo '<p>';
|
642 |
-
|
643 |
echo ' ';
|
644 |
-
|
645 |
echo '</p>';
|
646 |
|
647 |
-
echo '<form action="
|
648 |
settings_fields($this->option_pw_force_change_name);
|
649 |
|
650 |
$this->echo_div();
|
@@ -653,13 +688,14 @@ class login_security_solution_admin extends login_security_solution {
|
|
653 |
. $this->hsc_utf8($this->option_pw_force_change_name)
|
654 |
. '[' . $this->hsc_utf8($this->key_checkbox_require)
|
655 |
. ']" /> ';
|
656 |
-
|
657 |
echo '</strong></p>';
|
658 |
|
|
|
659 |
submit_button(
|
660 |
$this->text_button_require,
|
661 |
'primary',
|
662 |
-
$this->
|
663 |
);
|
664 |
|
665 |
echo "</div>\n";
|
@@ -671,13 +707,14 @@ class login_security_solution_admin extends login_security_solution {
|
|
671 |
. $this->hsc_utf8($this->option_pw_force_change_name)
|
672 |
. '[' . $this->hsc_utf8($this->key_checkbox_remind)
|
673 |
. ']" /> ';
|
674 |
-
|
675 |
echo '</p>';
|
676 |
|
|
|
677 |
submit_button(
|
678 |
$this->text_button_remind,
|
679 |
'secondary',
|
680 |
-
$this->
|
681 |
);
|
682 |
|
683 |
echo "</div>\n";
|
@@ -702,40 +739,50 @@ class login_security_solution_admin extends login_security_solution {
|
|
702 |
&& !empty($in['submit'])
|
703 |
&& is_scalar($in['submit']))
|
704 |
{
|
705 |
-
$crossed = __("You have checked a box that does not correspond with the button you pressed. Please check and press buttons inside the same section.", self::ID);
|
|
|
|
|
706 |
|
707 |
switch ($in['submit']) {
|
708 |
case $this->text_button_remind:
|
709 |
if (!empty($in[$this->key_checkbox_require])) {
|
710 |
add_settings_error($this->option_pw_force_change_name,
|
711 |
-
$this->option_pw_force_change_name,
|
|
|
712 |
} elseif (empty($in[$this->key_checkbox_remind])) {
|
713 |
-
add_settings_error($this->option_pw_force_change_name,
|
|
|
|
|
714 |
} else {
|
715 |
// Translaton already in WP.
|
716 |
add_settings_error($this->option_pw_force_change_name,
|
717 |
$this->option_pw_force_change_name,
|
718 |
-
__(
|
|
|
719 |
$out = 1;
|
720 |
}
|
721 |
break;
|
722 |
case $this->text_button_require:
|
723 |
if (!empty($in[$this->key_checkbox_remind])) {
|
724 |
add_settings_error($this->option_pw_force_change_name,
|
725 |
-
$this->option_pw_force_change_name,
|
|
|
726 |
} elseif (empty($in[$this->key_checkbox_require])) {
|
727 |
-
add_settings_error($this->option_pw_force_change_name,
|
|
|
|
|
728 |
} else {
|
729 |
$result = $this->force_change_for_all();
|
730 |
if ($result === true) {
|
731 |
// Translaton already in WP.
|
732 |
add_settings_error($this->option_pw_force_change_name,
|
733 |
$this->option_pw_force_change_name,
|
734 |
-
__(
|
735 |
$out = 1;
|
736 |
} else {
|
737 |
add_settings_error($this->option_pw_force_change_name,
|
738 |
-
$this->option_pw_force_change_name,
|
|
|
739 |
}
|
740 |
}
|
741 |
break;
|
@@ -756,21 +803,24 @@ class login_security_solution_admin extends login_security_solution {
|
|
756 |
* @return void
|
757 |
*/
|
758 |
public function admin_notices() {
|
759 |
-
if (!current_user_can(
|
760 |
return;
|
761 |
}
|
762 |
|
763 |
echo '<div class="error">';
|
764 |
|
765 |
echo '<p><strong>';
|
766 |
-
|
767 |
echo '</strong></p>';
|
768 |
|
769 |
echo '<p><strong>';
|
770 |
-
|
771 |
echo '</strong></p>';
|
772 |
|
773 |
-
echo '<p><strong><a href="
|
|
|
|
|
|
|
774 |
|
775 |
echo "</div>\n";
|
776 |
}
|
@@ -791,7 +841,7 @@ class login_security_solution_admin extends login_security_solution {
|
|
791 |
protected function force_change_for_all() {
|
792 |
global $user_ID, $wpdb;
|
793 |
|
794 |
-
if (!current_user_can(
|
795 |
// Translaton already in WP.
|
796 |
return __('You do not have sufficient permissions to access this page.');
|
797 |
}
|
6 |
*
|
7 |
* @package login-security-solution
|
8 |
* @link http://wordpress.org/extend/plugins/login-security-solution/
|
|
|
9 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2
|
10 |
* @author Daniel Convissor <danielc@analysisandsolutions.com>
|
11 |
* @copyright The Analysis and Solutions Company, 2012
|
17 |
*
|
18 |
* @package login-security-solution
|
19 |
* @link http://wordpress.org/extend/plugins/login-security-solution/
|
|
|
20 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2
|
21 |
* @author Daniel Convissor <danielc@analysisandsolutions.com>
|
22 |
* @copyright The Analysis and Solutions Company, 2012
|
23 |
*/
|
24 |
class login_security_solution_admin extends login_security_solution {
|
25 |
+
/**
|
26 |
+
* The WP privilege level required to use the admin interface
|
27 |
+
* @var string
|
28 |
+
*/
|
29 |
+
protected $capability_required;
|
30 |
+
|
31 |
/**
|
32 |
* Metadata and labels for each element of the plugin's options
|
33 |
* @var array
|
34 |
*/
|
35 |
protected $fields;
|
36 |
|
37 |
+
/**
|
38 |
+
* URI for the forms' action attributes
|
39 |
+
* @var string
|
40 |
+
*/
|
41 |
+
protected $form_action;
|
42 |
+
|
43 |
/**
|
44 |
* Key for the change password "don't remind me" checkbox
|
45 |
* @var string
|
58 |
*/
|
59 |
protected $option_pw_force_change_name;
|
60 |
|
61 |
+
/**
|
62 |
+
* Name of the page holding the options
|
63 |
+
* @var string
|
64 |
+
*/
|
65 |
+
protected $page_options;
|
66 |
+
|
67 |
/**
|
68 |
* Text for the plugin's password change page "don't remind me" button
|
69 |
* @var string
|
106 |
// Combine plugin's name with translation already in WP.
|
107 |
$this->text_settings = self::NAME . ' ' . __('Settings');
|
108 |
|
109 |
+
if (is_multisite()) {
|
110 |
+
$this->capability_required = 'manage_network_options';
|
111 |
+
$this->form_action = '../options.php';
|
112 |
+
$this->page_options = 'settings.php';
|
113 |
+
} else {
|
114 |
+
$this->capability_required = 'manage_options';
|
115 |
+
$this->form_action = 'options.php';
|
116 |
+
$this->page_options = 'options-general.php';
|
117 |
+
}
|
118 |
+
|
119 |
// NON-STANDARD: This is for the password change page.
|
120 |
$this->option_pw_force_change_name = self::ID . '-pw-force-change-done';
|
121 |
$this->text_pw_force_change = __('Change All Passwords', self::ID);
|
134 |
public function activate() {
|
135 |
global $wpdb;
|
136 |
|
137 |
+
if (is_multisite() && !is_network_admin()) {
|
138 |
+
die($this->hsc_utf8(sprintf(__("%s must be activated via the Network Admin interface when WordPress is in multistie network mode."), self::NAME)));
|
139 |
+
}
|
140 |
+
|
141 |
/*
|
142 |
* Create or alter the plugin's tables as needed.
|
143 |
*/
|
167 |
* Save this plugin's options to the database.
|
168 |
*/
|
169 |
|
170 |
+
if (is_multisite()) {
|
171 |
+
switch_to_blog(1);
|
172 |
+
}
|
173 |
update_option($this->option_name, $this->options);
|
174 |
add_option($this->option_pw_force_change_name, 0, '', 'no');
|
175 |
+
if (is_multisite()) {
|
176 |
+
restore_current_blog();
|
177 |
+
}
|
178 |
|
179 |
/*
|
180 |
* Store password hashes.
|
343 |
* plugin being displayed on WordPress' Plugins admin page.
|
344 |
*
|
345 |
* @param array $links the links generated thus far
|
|
|
346 |
* @return array
|
347 |
*/
|
348 |
+
public function plugin_action_links($links) {
|
349 |
+
$links[] = '<a href="' . $this->hsc_utf8($this->page_options)
|
350 |
+
. '?page=' . self::ID . '">'
|
351 |
+
. $this->hsc_utf8(__('Settings')) . '</a>';
|
352 |
+
|
353 |
+
// NON-STANDARD: This is for the password change page.
|
354 |
+
$links[] = '<a href="' . $this->hsc_utf8($this->page_options)
|
355 |
+
. '?page=' . $this->hsc_utf8($this->option_pw_force_change_name)
|
356 |
+
. '">' . $this->hsc_utf8($this->text_pw_force_change) . '</a>';
|
357 |
|
|
|
|
|
|
|
|
|
|
|
358 |
return $links;
|
359 |
}
|
360 |
|
365 |
* any admin page is rendered
|
366 |
*/
|
367 |
public function admin_menu() {
|
368 |
+
add_submenu_page(
|
369 |
+
$this->page_options,
|
370 |
$this->text_settings,
|
371 |
self::NAME,
|
372 |
+
$this->capability_required,
|
373 |
self::ID,
|
374 |
array(&$this, 'page_settings')
|
375 |
);
|
426 |
*/
|
427 |
public function page_settings() {
|
428 |
echo '<h2>' . $this->hsc_utf8($this->text_settings) . '</h2>';
|
429 |
+
echo '<form action="' . $this->hsc_utf8($this->form_action) . '" method="post">' . "\n";
|
430 |
settings_fields($this->option_name);
|
431 |
do_settings_sections(self::ID);
|
432 |
submit_button();
|
446 |
*/
|
447 |
public function section_login() {
|
448 |
echo '<p>';
|
449 |
+
echo $this->hsc_utf8(__("This plugin stores the IP address, username and password for each failed log in attempt.", self::ID));
|
450 |
echo ' ';
|
451 |
+
echo $this->hsc_utf8(__("The data from future login failures are compared against the historical data.", self::ID));
|
452 |
echo ' ';
|
453 |
+
echo $this->hsc_utf8(__("If any of the data points match, the plugin delays printing out the failure message.", self::ID));
|
454 |
echo ' ';
|
455 |
+
echo $this->hsc_utf8(__("The goal is for the responses to take so long that the attackers give up and go find an easier target.", self::ID));
|
456 |
echo ' ';
|
457 |
+
echo $this->hsc_utf8(__("The length of the delay is broken up into three tiers.", self::ID));
|
458 |
echo ' ';
|
459 |
+
echo $this->hsc_utf8(__("The amount of the delay increases in higher tiers.", self::ID));
|
460 |
echo ' ';
|
461 |
+
echo $this->hsc_utf8(__("The delay time within each tier is randomized to complicate profiling by attackers.", self::ID));
|
462 |
echo '</p>';
|
463 |
}
|
464 |
|
513 |
. $this->hsc_utf8($this->option_name)
|
514 |
. '[' . $this->hsc_utf8($name) . ']"'
|
515 |
. ' value="' . $this->hsc_utf8($this->options[$name]) . '" /> ';
|
516 |
+
echo $this->hsc_utf8($this->fields[$name]['text']
|
517 |
+
. ' ' . __('Default:', self::ID) . ' '
|
518 |
+
. $this->options_default[$name] . '.');
|
519 |
}
|
520 |
|
521 |
/**
|
548 |
|
549 |
if (!is_scalar($in[$name])) {
|
550 |
// Not translating this since only hackers will see it.
|
551 |
+
add_settings_error($this->option_name, $name,
|
552 |
+
$this->hsc_utf8("'" . $field['label'])
|
553 |
+
. "' was not a scalar, $default");
|
554 |
continue;
|
555 |
}
|
556 |
|
558 |
case 'bool':
|
559 |
if ($in[$name] != 0 && $in[$name] != 1) {
|
560 |
// Not translating this since only hackers will see it.
|
561 |
+
add_settings_error($this->option_name, $name,
|
562 |
+
$this->hsc_utf8("'" . $field['label']
|
563 |
+
. "' must be '0' or '1', $default"));
|
564 |
continue 2;
|
565 |
}
|
566 |
break;
|
567 |
case 'int':
|
568 |
if (!ctype_digit($in[$name])) {
|
569 |
+
add_settings_error($this->option_name, $name,
|
570 |
+
$this->hsc_utf8("'" . $field['label'] . "' "
|
571 |
+
. __("must be an integer,", self::ID)
|
572 |
+
. ' ' . $default));
|
573 |
continue 2;
|
574 |
}
|
575 |
if (array_key_exists('greater_than', $field)
|
576 |
&& $in[$name] < $field['greater_than'])
|
577 |
{
|
578 |
+
add_settings_error($this->option_name, $name,
|
579 |
+
$this->hsc_utf8("'" . $field['label'] . "' "
|
580 |
+
. sprintf($gt_format, $field['greater_than'])
|
581 |
+
. ' ' . $default));
|
|
|
582 |
continue 2;
|
583 |
}
|
584 |
break;
|
589 |
// Special check to make sure Delay Tier 3 > Delay Tier 2.
|
590 |
$name = 'login_fail_tier_3';
|
591 |
if ($out[$name] <= $out['login_fail_tier_2']) {
|
592 |
+
add_settings_error($this->option_name, $name,
|
593 |
+
$this->hsc_utf8("'" . $this->fields[$name]['label'] . "' "
|
594 |
+
. sprintf($gt_format, $this->fields['login_fail_tier_2']['label'])
|
595 |
+
. ' ' . $default));
|
|
|
596 |
|
597 |
$out[$name] = $out['login_fail_tier_2'] + 5;
|
598 |
}
|
600 |
// Speical check to ensure reuse count is set if aging is enabled.
|
601 |
$name = 'pw_reuse_count';
|
602 |
if ($out['pw_change_days'] && !$out[$name]) {
|
603 |
+
add_settings_error($this->option_name, $name,
|
604 |
+
$this->hsc_utf8("'" . $this->fields[$name]['label'] . "' "
|
605 |
+
. sprintf($gt_format, 1)
|
606 |
+
. ' ' . $default));
|
607 |
|
608 |
$out[$name] = 5;
|
609 |
}
|
622 |
* any admin page is rendered
|
623 |
*/
|
624 |
public function admin_menu_pw_force_change() {
|
625 |
+
add_submenu_page(
|
626 |
+
$this->page_options,
|
627 |
$this->text_pw_force_change,
|
628 |
'',
|
629 |
+
$this->capability_required,
|
630 |
$this->option_pw_force_change_name,
|
631 |
array(&$this, 'page_pw_force_change')
|
632 |
);
|
668 |
echo '<h2>' . $this->hsc_utf8($this->text_pw_force_change) . '</h2>';
|
669 |
|
670 |
echo '<p>';
|
671 |
+
echo $this->hsc_utf8(__("There may be cases where everyone's password should be reset.", self::ID));
|
672 |
echo ' ';
|
673 |
+
echo $this->hsc_utf8(sprintf(__("This page, provided by the %s plugin, offers that functionality.", self::ID), self::NAME));
|
674 |
echo '</p>';
|
675 |
|
676 |
echo '<p>';
|
677 |
+
echo $this->hsc_utf8(__("Submitting this form sets a flag that forces all users to utilize WordPress' built in password reset functionality.", self::ID));
|
678 |
echo ' ';
|
679 |
+
echo $this->hsc_utf8(__("Users who are presently logged in will be logged out the next time they view a page that requires authentication.", self::ID));
|
680 |
echo '</p>';
|
681 |
|
682 |
+
echo '<form action="' . $this->hsc_utf8($this->form_action) . '" method="post">' . "\n";
|
683 |
settings_fields($this->option_pw_force_change_name);
|
684 |
|
685 |
$this->echo_div();
|
688 |
. $this->hsc_utf8($this->option_pw_force_change_name)
|
689 |
. '[' . $this->hsc_utf8($this->key_checkbox_require)
|
690 |
. ']" /> ';
|
691 |
+
echo $this->hsc_utf8(__("Confirm that you want to force all users to change their passwords by checking this box, then click the button, below.", self::ID));
|
692 |
echo '</strong></p>';
|
693 |
|
694 |
+
// This function escapes output.
|
695 |
submit_button(
|
696 |
$this->text_button_require,
|
697 |
'primary',
|
698 |
+
$this->option_pw_force_change_name . '[submit]'
|
699 |
);
|
700 |
|
701 |
echo "</div>\n";
|
707 |
. $this->hsc_utf8($this->option_pw_force_change_name)
|
708 |
. '[' . $this->hsc_utf8($this->key_checkbox_remind)
|
709 |
. ']" /> ';
|
710 |
+
echo $this->hsc_utf8(__("No thanks. I know what I'm doing. Please don't remind me about this.", self::ID));
|
711 |
echo '</p>';
|
712 |
|
713 |
+
// This function escapes output.
|
714 |
submit_button(
|
715 |
$this->text_button_remind,
|
716 |
'secondary',
|
717 |
+
$this->option_pw_force_change_name . '[submit]'
|
718 |
);
|
719 |
|
720 |
echo "</div>\n";
|
739 |
&& !empty($in['submit'])
|
740 |
&& is_scalar($in['submit']))
|
741 |
{
|
742 |
+
$crossed = $this->hsc_utf8(__("You have checked a box that does not correspond with the button you pressed. Please check and press buttons inside the same section.", self::ID));
|
743 |
+
|
744 |
+
$confirm = __("Please confirm that you really want to do this. Put a check in the '%s' box before hitting the submit button.", self::ID);
|
745 |
|
746 |
switch ($in['submit']) {
|
747 |
case $this->text_button_remind:
|
748 |
if (!empty($in[$this->key_checkbox_require])) {
|
749 |
add_settings_error($this->option_pw_force_change_name,
|
750 |
+
$this->option_pw_force_change_name,
|
751 |
+
$crossed);
|
752 |
} elseif (empty($in[$this->key_checkbox_remind])) {
|
753 |
+
add_settings_error($this->option_pw_force_change_name,
|
754 |
+
$this->option_pw_force_change_name,
|
755 |
+
$this->hsc_utf8(sprintf($confirm, "No thanks")));
|
756 |
} else {
|
757 |
// Translaton already in WP.
|
758 |
add_settings_error($this->option_pw_force_change_name,
|
759 |
$this->option_pw_force_change_name,
|
760 |
+
$this->hsc_utf8(__("Success!")),
|
761 |
+
'updated');
|
762 |
$out = 1;
|
763 |
}
|
764 |
break;
|
765 |
case $this->text_button_require:
|
766 |
if (!empty($in[$this->key_checkbox_remind])) {
|
767 |
add_settings_error($this->option_pw_force_change_name,
|
768 |
+
$this->option_pw_force_change_name,
|
769 |
+
$crossed);
|
770 |
} elseif (empty($in[$this->key_checkbox_require])) {
|
771 |
+
add_settings_error($this->option_pw_force_change_name,
|
772 |
+
$this->option_pw_force_change_name,
|
773 |
+
$this->hsc_utf8(sprintf($confirm, "Confirm")));
|
774 |
} else {
|
775 |
$result = $this->force_change_for_all();
|
776 |
if ($result === true) {
|
777 |
// Translaton already in WP.
|
778 |
add_settings_error($this->option_pw_force_change_name,
|
779 |
$this->option_pw_force_change_name,
|
780 |
+
$this->hsc_utf8(__("Success!")), 'updated');
|
781 |
$out = 1;
|
782 |
} else {
|
783 |
add_settings_error($this->option_pw_force_change_name,
|
784 |
+
$this->option_pw_force_change_name,
|
785 |
+
$this->hsc_utf8($result));
|
786 |
}
|
787 |
}
|
788 |
break;
|
803 |
* @return void
|
804 |
*/
|
805 |
public function admin_notices() {
|
806 |
+
if (!current_user_can($this->capability_required)) {
|
807 |
return;
|
808 |
}
|
809 |
|
810 |
echo '<div class="error">';
|
811 |
|
812 |
echo '<p><strong>';
|
813 |
+
echo $this->hsc_utf8(__("You have not asked your users to change their passwords since the plugin was activated. Most users have weak passwords. This plugin's password policies protect your site from brute force attacks. Please improve security for everyone on the Internet by making all users pick new, strong, passwords.", self::ID));
|
814 |
echo '</strong></p>';
|
815 |
|
816 |
echo '<p><strong>';
|
817 |
+
echo $this->hsc_utf8(__("Speaking of which, do YOU have a strong password? Make sure by changing yours once you've submitted the Change All Passwords form.", self::ID));
|
818 |
echo '</strong></p>';
|
819 |
|
820 |
+
echo '<p><strong><a href="' . $this->hsc_utf8($this->page_options)
|
821 |
+
. '?page=' . $this->hsc_utf8($this->option_pw_force_change_name)
|
822 |
+
. '">' . $this->hsc_utf8($this->text_pw_force_change)
|
823 |
+
. "</a></strong></p>\n";
|
824 |
|
825 |
echo "</div>\n";
|
826 |
}
|
841 |
protected function force_change_for_all() {
|
842 |
global $user_ID, $wpdb;
|
843 |
|
844 |
+
if (!current_user_can($this->capability_required)) {
|
845 |
// Translaton already in WP.
|
846 |
return __('You do not have sufficient permissions to access this page.');
|
847 |
}
|
languages/login-security-solution.pot
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
# This file is distributed under the same license as the Login Security Solution package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
-
"Project-Id-Version: Login Security Solution 0.0
|
6 |
"Report-Msgid-Bugs-To: http://wordpress.org/tag/login-security-solution\n"
|
7 |
-
"POT-Creation-Date: 2012-
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
@@ -12,170 +12,168 @@ msgstr ""
|
|
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
|
15 |
-
#: login-security-solution.php:
|
16 |
msgid "Invalid username or password."
|
17 |
msgstr ""
|
18 |
|
19 |
-
#: login-security-solution.php:
|
20 |
-
#: tests/LoginErrorsTest.php:
|
21 |
msgid "Password reset is not allowed for this user"
|
22 |
msgstr ""
|
23 |
|
24 |
-
#: login-security-solution.php:
|
25 |
msgid "It has been over %d minutes since your last action."
|
26 |
msgstr ""
|
27 |
|
28 |
-
#: login-security-solution.php:
|
29 |
msgid "Please log back in."
|
30 |
msgstr ""
|
31 |
|
32 |
-
#: login-security-solution.php:
|
33 |
msgid "The grace period for changing your password has expired."
|
34 |
msgstr ""
|
35 |
|
36 |
-
#: login-security-solution.php:
|
37 |
msgid "Please submit this form to reset your password."
|
38 |
msgstr ""
|
39 |
|
40 |
-
#: login-security-solution.php:
|
41 |
msgid "Your password must be reset."
|
42 |
msgstr ""
|
43 |
|
44 |
-
#: login-security-solution.php:
|
45 |
msgid "Please submit this form to reset it."
|
46 |
msgstr ""
|
47 |
|
48 |
-
#: login-security-solution.php:
|
49 |
msgid "Your password has expired. Please log and change it."
|
50 |
msgstr ""
|
51 |
|
52 |
-
#: login-security-solution.php:
|
53 |
msgid "We provide a %d minute grace period to do so."
|
54 |
msgstr ""
|
55 |
|
56 |
-
#: login-security-solution.php:
|
57 |
-
#: tests/LoginMessageTest.php:
|
58 |
msgid "The site is undergoing maintenance."
|
59 |
msgstr ""
|
60 |
|
61 |
-
#: login-security-solution.php:
|
62 |
-
#: tests/LoginMessageTest.php:
|
63 |
msgid "Please try again later."
|
64 |
msgstr ""
|
65 |
|
66 |
-
#: login-security-solution.php:
|
67 |
-
msgid "
|
68 |
msgstr ""
|
69 |
|
70 |
-
#: login-security-solution.php:
|
|
|
|
|
|
|
|
|
71 |
msgid "Component Count Value from Current Attempt"
|
72 |
msgstr ""
|
73 |
|
74 |
-
#: login-security-solution.php:
|
75 |
msgid "Network IP %5d %s"
|
76 |
msgstr ""
|
77 |
|
78 |
-
#: login-security-solution.php:
|
79 |
msgid "Username %5d %s"
|
80 |
msgstr ""
|
81 |
|
82 |
-
#: login-security-solution.php:
|
83 |
msgid "Password MD5 %5d %s"
|
84 |
msgstr ""
|
85 |
|
86 |
-
#: login-security-solution.php:
|
87 |
msgid "Your website, %s, may have been broken in to."
|
88 |
msgstr ""
|
89 |
|
90 |
-
#: login-security-solution.php:
|
91 |
msgid ""
|
92 |
"Someone just logged in using the following components. Prior to that, some "
|
93 |
"combination of those components were a part of %d failed attempts to log in "
|
94 |
"during the past %d minutes:"
|
95 |
msgstr ""
|
96 |
|
97 |
-
#: login-security-solution.php:
|
98 |
msgid ""
|
99 |
"The user has been logged out and will be required to confirm their identity "
|
100 |
"via the password reset functionality."
|
101 |
msgstr ""
|
102 |
|
103 |
-
#: login-security-solution.php:
|
104 |
msgid "Your website, %s, is undergoing a brute force attack."
|
105 |
msgstr ""
|
106 |
|
107 |
-
#: login-security-solution.php:
|
108 |
msgid ""
|
109 |
"There have been at least %d failed attempts to log in during the past %d "
|
110 |
"minutes that used one or more of the following components:"
|
111 |
msgstr ""
|
112 |
|
113 |
-
#: login-security-solution.php:
|
114 |
msgid ""
|
115 |
"The %s plugin for WordPress is repelling the attack by making their login "
|
116 |
"failures take a very long time."
|
117 |
msgstr ""
|
118 |
|
119 |
-
#: login-security-solution.php:
|
120 |
-
msgid "
|
121 |
msgstr ""
|
122 |
|
123 |
-
#: login-security-solution.php:
|
124 |
-
msgid "
|
125 |
msgstr ""
|
126 |
|
127 |
-
#: login-security-solution.php:
|
128 |
-
msgid "
|
129 |
msgstr ""
|
130 |
|
131 |
-
#: login-security-solution.php:
|
132 |
-
|
133 |
-
msgid "<strong>ERROR</strong>: Password is too short."
|
134 |
msgstr ""
|
135 |
|
136 |
-
#: login-security-solution.php:
|
137 |
-
msgid ""
|
138 |
-
"<strong>ERROR</strong>: Passwords must either contain numbers or be %d "
|
139 |
-
"characters long."
|
140 |
msgstr ""
|
141 |
|
142 |
-
#: login-security-solution.php:
|
143 |
msgid ""
|
144 |
-
"
|
145 |
-
"
|
146 |
msgstr ""
|
147 |
|
148 |
-
#: login-security-solution.php:
|
149 |
msgid ""
|
150 |
-
"
|
151 |
-
"
|
152 |
msgstr ""
|
153 |
|
154 |
-
#: login-security-solution.php:
|
155 |
-
msgid "
|
156 |
msgstr ""
|
157 |
|
158 |
-
#: login-security-solution.php:
|
159 |
-
msgid ""
|
160 |
-
"<strong>ERROR</strong>: Passwords can't have that many sequential characters."
|
161 |
msgstr ""
|
162 |
|
163 |
-
#: login-security-solution.php:
|
164 |
-
|
165 |
-
msgid "<strong>ERROR</strong>: Passwords can't contain user data."
|
166 |
msgstr ""
|
167 |
|
168 |
-
#: login-security-solution.php:
|
169 |
-
msgid "
|
170 |
msgstr ""
|
171 |
|
172 |
-
#: login-security-solution.php:
|
173 |
-
msgid "
|
174 |
msgstr ""
|
175 |
|
176 |
-
#: login-security-solution.php:
|
177 |
-
msgid ""
|
178 |
-
"<strong>ERROR</strong>: Passwords can't be variations of dictionary words."
|
179 |
msgstr ""
|
180 |
|
181 |
#. Plugin Name of the plugin/theme
|
2 |
# This file is distributed under the same license as the Login Security Solution package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
+
"Project-Id-Version: Login Security Solution 0.4.0\n"
|
6 |
"Report-Msgid-Bugs-To: http://wordpress.org/tag/login-security-solution\n"
|
7 |
+
"POT-Creation-Date: 2012-04-17 21:46:55+00:00\n"
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
|
15 |
+
#: login-security-solution.php:431
|
16 |
msgid "Invalid username or password."
|
17 |
msgstr ""
|
18 |
|
19 |
+
#: login-security-solution.php:437 tests/LoginErrorsTest.php:117
|
20 |
+
#: tests/LoginErrorsTest.php:129
|
21 |
msgid "Password reset is not allowed for this user"
|
22 |
msgstr ""
|
23 |
|
24 |
+
#: login-security-solution.php:462 tests/LoginMessageTest.php:61
|
25 |
msgid "It has been over %d minutes since your last action."
|
26 |
msgstr ""
|
27 |
|
28 |
+
#: login-security-solution.php:463 tests/LoginMessageTest.php:62
|
29 |
msgid "Please log back in."
|
30 |
msgstr ""
|
31 |
|
32 |
+
#: login-security-solution.php:466 tests/LoginMessageTest.php:72
|
33 |
msgid "The grace period for changing your password has expired."
|
34 |
msgstr ""
|
35 |
|
36 |
+
#: login-security-solution.php:467 tests/LoginMessageTest.php:73
|
37 |
msgid "Please submit this form to reset your password."
|
38 |
msgstr ""
|
39 |
|
40 |
+
#: login-security-solution.php:470 tests/LoginMessageTest.php:83
|
41 |
msgid "Your password must be reset."
|
42 |
msgstr ""
|
43 |
|
44 |
+
#: login-security-solution.php:471 tests/LoginMessageTest.php:84
|
45 |
msgid "Please submit this form to reset it."
|
46 |
msgstr ""
|
47 |
|
48 |
+
#: login-security-solution.php:474 tests/LoginMessageTest.php:94
|
49 |
msgid "Your password has expired. Please log and change it."
|
50 |
msgstr ""
|
51 |
|
52 |
+
#: login-security-solution.php:475 tests/LoginMessageTest.php:95
|
53 |
msgid "We provide a %d minute grace period to do so."
|
54 |
msgstr ""
|
55 |
|
56 |
+
#: login-security-solution.php:481 tests/LoginMessageTest.php:109
|
57 |
+
#: tests/LoginMessageTest.php:124
|
58 |
msgid "The site is undergoing maintenance."
|
59 |
msgstr ""
|
60 |
|
61 |
+
#: login-security-solution.php:482 tests/LoginMessageTest.php:110
|
62 |
+
#: tests/LoginMessageTest.php:125
|
63 |
msgid "Please try again later."
|
64 |
msgstr ""
|
65 |
|
66 |
+
#: login-security-solution.php:541
|
67 |
+
msgid "Passwords can not be reused."
|
68 |
msgstr ""
|
69 |
|
70 |
+
#: login-security-solution.php:686
|
71 |
+
msgid "ERROR"
|
72 |
+
msgstr ""
|
73 |
+
|
74 |
+
#: login-security-solution.php:806
|
75 |
msgid "Component Count Value from Current Attempt"
|
76 |
msgstr ""
|
77 |
|
78 |
+
#: login-security-solution.php:808
|
79 |
msgid "Network IP %5d %s"
|
80 |
msgstr ""
|
81 |
|
82 |
+
#: login-security-solution.php:810
|
83 |
msgid "Username %5d %s"
|
84 |
msgstr ""
|
85 |
|
86 |
+
#: login-security-solution.php:812
|
87 |
msgid "Password MD5 %5d %s"
|
88 |
msgstr ""
|
89 |
|
90 |
+
#: login-security-solution.php:1593
|
91 |
msgid "Your website, %s, may have been broken in to."
|
92 |
msgstr ""
|
93 |
|
94 |
+
#: login-security-solution.php:1596
|
95 |
msgid ""
|
96 |
"Someone just logged in using the following components. Prior to that, some "
|
97 |
"combination of those components were a part of %d failed attempts to log in "
|
98 |
"during the past %d minutes:"
|
99 |
msgstr ""
|
100 |
|
101 |
+
#: login-security-solution.php:1601
|
102 |
msgid ""
|
103 |
"The user has been logged out and will be required to confirm their identity "
|
104 |
"via the password reset functionality."
|
105 |
msgstr ""
|
106 |
|
107 |
+
#: login-security-solution.php:1630
|
108 |
msgid "Your website, %s, is undergoing a brute force attack."
|
109 |
msgstr ""
|
110 |
|
111 |
+
#: login-security-solution.php:1633
|
112 |
msgid ""
|
113 |
"There have been at least %d failed attempts to log in during the past %d "
|
114 |
"minutes that used one or more of the following components:"
|
115 |
msgstr ""
|
116 |
|
117 |
+
#: login-security-solution.php:1638
|
118 |
msgid ""
|
119 |
"The %s plugin for WordPress is repelling the attack by making their login "
|
120 |
"failures take a very long time."
|
121 |
msgstr ""
|
122 |
|
123 |
+
#: login-security-solution.php:1949
|
124 |
+
msgid "Password not set."
|
125 |
msgstr ""
|
126 |
|
127 |
+
#: login-security-solution.php:1964
|
128 |
+
msgid "Passwords must be strings."
|
129 |
msgstr ""
|
130 |
|
131 |
+
#: login-security-solution.php:1982
|
132 |
+
msgid "Passwords must use ASCII characters."
|
133 |
msgstr ""
|
134 |
|
135 |
+
#: login-security-solution.php:2001
|
136 |
+
msgid "Password is too short."
|
|
|
137 |
msgstr ""
|
138 |
|
139 |
+
#: login-security-solution.php:2010
|
140 |
+
msgid "Passwords must either contain numbers or be %d characters long."
|
|
|
|
|
141 |
msgstr ""
|
142 |
|
143 |
+
#: login-security-solution.php:2019
|
144 |
msgid ""
|
145 |
+
"Passwords must either contain punctuation marks / symbols or be %d "
|
146 |
+
"characters long."
|
147 |
msgstr ""
|
148 |
|
149 |
+
#: login-security-solution.php:2028
|
150 |
msgid ""
|
151 |
+
"Passwords must either contain upper-case and lower-case letters or be %d "
|
152 |
+
"characters long."
|
153 |
msgstr ""
|
154 |
|
155 |
+
#: login-security-solution.php:2038
|
156 |
+
msgid "Passwords can't be sequential keys."
|
157 |
msgstr ""
|
158 |
|
159 |
+
#: login-security-solution.php:2047
|
160 |
+
msgid "Passwords can't have that many sequential characters."
|
|
|
161 |
msgstr ""
|
162 |
|
163 |
+
#: login-security-solution.php:2063
|
164 |
+
msgid "Passwords can't contain user data."
|
|
|
165 |
msgstr ""
|
166 |
|
167 |
+
#: login-security-solution.php:2074
|
168 |
+
msgid "Passwords can't contain site info."
|
169 |
msgstr ""
|
170 |
|
171 |
+
#: login-security-solution.php:2083
|
172 |
+
msgid "Password is too common."
|
173 |
msgstr ""
|
174 |
|
175 |
+
#: login-security-solution.php:2092
|
176 |
+
msgid "Passwords can't be variations of dictionary words."
|
|
|
177 |
msgstr ""
|
178 |
|
179 |
#. Plugin Name of the plugin/theme
|
login-security-solution.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
* Description: Requires very strong passwords, repels brute force login attacks, prevents login information disclosures, expires idle sessions, notifies admins of attacks and breaches, permits administrators to disable logins for maintenance or emergency reasons and reset all passwords.
|
7 |
*
|
8 |
* Plugin URI: http://wordpress.org/extend/plugins/login-security-solution/
|
9 |
-
* Version: 0.
|
10 |
* Author: Daniel Convissor
|
11 |
* Author URI: http://www.analysisandsolutions.com/
|
12 |
* License: GPLv2
|
@@ -23,7 +23,6 @@ $GLOBALS['login_security_solution'] = new login_security_solution;
|
|
23 |
*
|
24 |
* @package login-security-solution
|
25 |
* @link http://wordpress.org/extend/plugins/login-security-solution/
|
26 |
-
* @version 0.0.1
|
27 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2
|
28 |
* @author Daniel Convissor <danielc@analysisandsolutions.com>
|
29 |
* @copyright The Analysis and Solutions Company, 2012
|
@@ -205,9 +204,19 @@ class login_security_solution {
|
|
205 |
require_once dirname(__FILE__) . '/admin.inc';
|
206 |
$admin = new login_security_solution_admin;
|
207 |
|
208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
209 |
add_action('admin_init', array(&$admin, 'admin_init'));
|
210 |
-
add_filter(
|
211 |
|
212 |
register_activation_hook(__FILE__, array(&$admin, 'activate'));
|
213 |
if ($this->options['deactivate_deletes_data']) {
|
@@ -215,11 +224,11 @@ class login_security_solution {
|
|
215 |
}
|
216 |
|
217 |
// NON-STANDARD: This is for the password change page.
|
218 |
-
add_action(
|
219 |
-
add_action('admin_init', array(&$admin, 'admin_init_pw_force_change'));
|
220 |
if (!$admin->was_pw_force_change_done()) {
|
221 |
-
add_action(
|
222 |
}
|
|
|
223 |
}
|
224 |
}
|
225 |
|
@@ -419,13 +428,13 @@ class login_security_solution {
|
|
419 |
$user_pass = empty($_POST['pwd']) ? '' : $_POST['pwd'];
|
420 |
$this->process_login_fail($user_name, $user_pass);
|
421 |
$this->load_plugin_textdomain();
|
422 |
-
return __('Invalid username or password.', self::ID);
|
423 |
}
|
424 |
|
425 |
$codes_to_cloak = array('invalid_email', 'invalidcombo');
|
426 |
if (array_intersect($error_codes, $codes_to_cloak)) {
|
427 |
// This text is lifted directly from WordPress.
|
428 |
-
return __('Password reset is not allowed for this user');
|
429 |
}
|
430 |
|
431 |
return $out;
|
@@ -529,7 +538,7 @@ class login_security_solution {
|
|
529 |
if ($this->is_pw_reused($user->user_pass, $user->ID)) {
|
530 |
$this->load_plugin_textdomain();
|
531 |
$errors->add(self::ID,
|
532 |
-
__("
|
533 |
array('form-field' => 'pass1')
|
534 |
);
|
535 |
return false;
|
@@ -667,6 +676,18 @@ class login_security_solution {
|
|
667 |
return delete_user_meta($user_ID, $this->umk_grace_period);
|
668 |
}
|
669 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
670 |
/**
|
671 |
* Obtains the IP address from $_SERVER['REMOTE_ADDR']
|
672 |
*
|
@@ -1665,6 +1686,11 @@ class login_security_solution {
|
|
1665 |
|
1666 |
if ($sleep) {
|
1667 |
if (!$this->testing) {
|
|
|
|
|
|
|
|
|
|
|
1668 |
// Keep login failures from becoming denial of service attacks.
|
1669 |
mysql_close($wpdb->dbh);
|
1670 |
|
@@ -1920,7 +1946,7 @@ class login_security_solution {
|
|
1920 |
if (empty($user->user_pass)) {
|
1921 |
if ($errors !== null) {
|
1922 |
$errors->add(self::ID,
|
1923 |
-
__("
|
1924 |
array('form-field' => 'pass1')
|
1925 |
);
|
1926 |
}
|
@@ -1935,7 +1961,7 @@ class login_security_solution {
|
|
1935 |
if (!is_string($pw)) {
|
1936 |
if ($errors !== null) {
|
1937 |
$errors->add(self::ID,
|
1938 |
-
__("
|
1939 |
array('form-field' => 'pass1')
|
1940 |
);
|
1941 |
}
|
@@ -1953,7 +1979,7 @@ class login_security_solution {
|
|
1953 |
{
|
1954 |
if ($errors !== null) {
|
1955 |
$errors->add(self::ID,
|
1956 |
-
__("
|
1957 |
array('form-field' => 'pass1')
|
1958 |
);
|
1959 |
}
|
@@ -1972,7 +1998,7 @@ class login_security_solution {
|
|
1972 |
if ($length < $this->options['pw_length']) {
|
1973 |
if ($errors !== null) {
|
1974 |
$errors->add(self::ID,
|
1975 |
-
__("
|
1976 |
array('form-field' => 'pass1')
|
1977 |
);
|
1978 |
}
|
@@ -1981,7 +2007,7 @@ class login_security_solution {
|
|
1981 |
if ($enforce_complexity && $this->is_pw_missing_numeric($pw)) {
|
1982 |
if ($errors !== null) {
|
1983 |
$errors->add(self::ID,
|
1984 |
-
sprintf(__("
|
1985 |
array('form-field' => 'pass1')
|
1986 |
);
|
1987 |
}
|
@@ -1990,7 +2016,7 @@ class login_security_solution {
|
|
1990 |
if ($enforce_complexity && $this->is_pw_missing_punct_chars($pw)) {
|
1991 |
if ($errors !== null) {
|
1992 |
$errors->add(self::ID,
|
1993 |
-
sprintf(__("
|
1994 |
array('form-field' => 'pass1')
|
1995 |
);
|
1996 |
}
|
@@ -1999,7 +2025,7 @@ class login_security_solution {
|
|
1999 |
if ($enforce_complexity && $this->is_pw_missing_upper_lower_chars($pw)) {
|
2000 |
if ($errors !== null) {
|
2001 |
$errors->add(self::ID,
|
2002 |
-
sprintf(__("
|
2003 |
array('form-field' => 'pass1')
|
2004 |
);
|
2005 |
}
|
@@ -2009,7 +2035,7 @@ class login_security_solution {
|
|
2009 |
if ($this->is_pw_sequential_file($pw)) {
|
2010 |
if ($errors !== null) {
|
2011 |
$errors->add(self::ID,
|
2012 |
-
__("
|
2013 |
array('form-field' => 'pass1')
|
2014 |
);
|
2015 |
}
|
@@ -2018,7 +2044,7 @@ class login_security_solution {
|
|
2018 |
if ($this->is_pw_sequential_codepoints($pw)) {
|
2019 |
if ($errors !== null) {
|
2020 |
$errors->add(self::ID,
|
2021 |
-
__("
|
2022 |
array('form-field' => 'pass1')
|
2023 |
);
|
2024 |
}
|
@@ -2034,7 +2060,7 @@ class login_security_solution {
|
|
2034 |
{
|
2035 |
if ($errors !== null) {
|
2036 |
$errors->add(self::ID,
|
2037 |
-
__("
|
2038 |
array('form-field' => 'pass1')
|
2039 |
);
|
2040 |
}
|
@@ -2045,7 +2071,7 @@ class login_security_solution {
|
|
2045 |
{
|
2046 |
if ($errors !== null) {
|
2047 |
$errors->add(self::ID,
|
2048 |
-
__("
|
2049 |
array('form-field' => 'pass1')
|
2050 |
);
|
2051 |
}
|
@@ -2054,7 +2080,7 @@ class login_security_solution {
|
|
2054 |
if ($all_tests && $this->is_pw_dictionary($pw)) {
|
2055 |
if ($errors !== null) {
|
2056 |
$errors->add(self::ID,
|
2057 |
-
__("
|
2058 |
array('form-field' => 'pass1')
|
2059 |
);
|
2060 |
}
|
@@ -2063,7 +2089,7 @@ class login_security_solution {
|
|
2063 |
if ($this->is_pw_dict_program($stripped)) {
|
2064 |
if ($errors !== null) {
|
2065 |
$errors->add(self::ID,
|
2066 |
-
__("
|
2067 |
array('form-field' => 'pass1')
|
2068 |
);
|
2069 |
}
|
6 |
* Description: Requires very strong passwords, repels brute force login attacks, prevents login information disclosures, expires idle sessions, notifies admins of attacks and breaches, permits administrators to disable logins for maintenance or emergency reasons and reset all passwords.
|
7 |
*
|
8 |
* Plugin URI: http://wordpress.org/extend/plugins/login-security-solution/
|
9 |
+
* Version: 0.4.0
|
10 |
* Author: Daniel Convissor
|
11 |
* Author URI: http://www.analysisandsolutions.com/
|
12 |
* License: GPLv2
|
23 |
*
|
24 |
* @package login-security-solution
|
25 |
* @link http://wordpress.org/extend/plugins/login-security-solution/
|
|
|
26 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2
|
27 |
* @author Daniel Convissor <danielc@analysisandsolutions.com>
|
28 |
* @copyright The Analysis and Solutions Company, 2012
|
204 |
require_once dirname(__FILE__) . '/admin.inc';
|
205 |
$admin = new login_security_solution_admin;
|
206 |
|
207 |
+
if (is_multisite()) {
|
208 |
+
$admin_menu = 'network_admin_menu';
|
209 |
+
$admin_notices = 'network_admin_notices';
|
210 |
+
$plugin_action_links = 'network_admin_plugin_action_links_login-security-solution/login-security-solution.php';
|
211 |
+
} else {
|
212 |
+
$admin_menu = 'admin_menu';
|
213 |
+
$admin_notices = 'admin_notices';
|
214 |
+
$plugin_action_links = 'plugin_action_links_login-security-solution/login-security-solution.php';
|
215 |
+
}
|
216 |
+
|
217 |
+
add_action($admin_menu, array(&$admin, 'admin_menu'));
|
218 |
add_action('admin_init', array(&$admin, 'admin_init'));
|
219 |
+
add_filter($plugin_action_links, array(&$admin, 'plugin_action_links'));
|
220 |
|
221 |
register_activation_hook(__FILE__, array(&$admin, 'activate'));
|
222 |
if ($this->options['deactivate_deletes_data']) {
|
224 |
}
|
225 |
|
226 |
// NON-STANDARD: This is for the password change page.
|
227 |
+
add_action($admin_menu, array(&$admin, 'admin_menu_pw_force_change'));
|
|
|
228 |
if (!$admin->was_pw_force_change_done()) {
|
229 |
+
add_action($admin_notices, array(&$admin, 'admin_notices'));
|
230 |
}
|
231 |
+
add_action('admin_init', array(&$admin, 'admin_init_pw_force_change'));
|
232 |
}
|
233 |
}
|
234 |
|
428 |
$user_pass = empty($_POST['pwd']) ? '' : $_POST['pwd'];
|
429 |
$this->process_login_fail($user_name, $user_pass);
|
430 |
$this->load_plugin_textdomain();
|
431 |
+
return $this->hsc_utf8(__('Invalid username or password.', self::ID));
|
432 |
}
|
433 |
|
434 |
$codes_to_cloak = array('invalid_email', 'invalidcombo');
|
435 |
if (array_intersect($error_codes, $codes_to_cloak)) {
|
436 |
// This text is lifted directly from WordPress.
|
437 |
+
return $this->hsc_utf8(__('Password reset is not allowed for this user'));
|
438 |
}
|
439 |
|
440 |
return $out;
|
538 |
if ($this->is_pw_reused($user->user_pass, $user->ID)) {
|
539 |
$this->load_plugin_textdomain();
|
540 |
$errors->add(self::ID,
|
541 |
+
$this->err(__("Passwords can not be reused.", self::ID)),
|
542 |
array('form-field' => 'pass1')
|
543 |
);
|
544 |
return false;
|
676 |
return delete_user_meta($user_ID, $this->umk_grace_period);
|
677 |
}
|
678 |
|
679 |
+
/**
|
680 |
+
* Safely composes translated error messages
|
681 |
+
*
|
682 |
+
* @param string $message the error message
|
683 |
+
* @return string
|
684 |
+
*/
|
685 |
+
protected function err($message) {
|
686 |
+
$error = $this->hsc_utf8(__("ERROR"));
|
687 |
+
$message = $this->hsc_utf8($message);
|
688 |
+
return "<strong>$error</strong>: $message";
|
689 |
+
}
|
690 |
+
|
691 |
/**
|
692 |
* Obtains the IP address from $_SERVER['REMOTE_ADDR']
|
693 |
*
|
1686 |
|
1687 |
if ($sleep) {
|
1688 |
if (!$this->testing) {
|
1689 |
+
if (is_multisite()) {
|
1690 |
+
// Get this cached before disconnecting the database.
|
1691 |
+
get_option('users_can_register');
|
1692 |
+
}
|
1693 |
+
|
1694 |
// Keep login failures from becoming denial of service attacks.
|
1695 |
mysql_close($wpdb->dbh);
|
1696 |
|
1946 |
if (empty($user->user_pass)) {
|
1947 |
if ($errors !== null) {
|
1948 |
$errors->add(self::ID,
|
1949 |
+
$this->err(__("Password not set.", self::ID)),
|
1950 |
array('form-field' => 'pass1')
|
1951 |
);
|
1952 |
}
|
1961 |
if (!is_string($pw)) {
|
1962 |
if ($errors !== null) {
|
1963 |
$errors->add(self::ID,
|
1964 |
+
$this->err(__("Passwords must be strings.", self::ID)),
|
1965 |
array('form-field' => 'pass1')
|
1966 |
);
|
1967 |
}
|
1979 |
{
|
1980 |
if ($errors !== null) {
|
1981 |
$errors->add(self::ID,
|
1982 |
+
$this->err(__("Passwords must use ASCII characters.", self::ID)),
|
1983 |
array('form-field' => 'pass1')
|
1984 |
);
|
1985 |
}
|
1998 |
if ($length < $this->options['pw_length']) {
|
1999 |
if ($errors !== null) {
|
2000 |
$errors->add(self::ID,
|
2001 |
+
$this->err(__("Password is too short.", self::ID)),
|
2002 |
array('form-field' => 'pass1')
|
2003 |
);
|
2004 |
}
|
2007 |
if ($enforce_complexity && $this->is_pw_missing_numeric($pw)) {
|
2008 |
if ($errors !== null) {
|
2009 |
$errors->add(self::ID,
|
2010 |
+
$this->err(sprintf(__("Passwords must either contain numbers or be %d characters long.", self::ID), $this->options['pw_complexity_exemption_length'])),
|
2011 |
array('form-field' => 'pass1')
|
2012 |
);
|
2013 |
}
|
2016 |
if ($enforce_complexity && $this->is_pw_missing_punct_chars($pw)) {
|
2017 |
if ($errors !== null) {
|
2018 |
$errors->add(self::ID,
|
2019 |
+
$this->err(sprintf(__("Passwords must either contain punctuation marks / symbols or be %d characters long.", self::ID), $this->options['pw_complexity_exemption_length'])),
|
2020 |
array('form-field' => 'pass1')
|
2021 |
);
|
2022 |
}
|
2025 |
if ($enforce_complexity && $this->is_pw_missing_upper_lower_chars($pw)) {
|
2026 |
if ($errors !== null) {
|
2027 |
$errors->add(self::ID,
|
2028 |
+
$this->err(sprintf(__("Passwords must either contain upper-case and lower-case letters or be %d characters long.", self::ID), $this->options['pw_complexity_exemption_length'])),
|
2029 |
array('form-field' => 'pass1')
|
2030 |
);
|
2031 |
}
|
2035 |
if ($this->is_pw_sequential_file($pw)) {
|
2036 |
if ($errors !== null) {
|
2037 |
$errors->add(self::ID,
|
2038 |
+
$this->err(__("Passwords can't be sequential keys.", self::ID)),
|
2039 |
array('form-field' => 'pass1')
|
2040 |
);
|
2041 |
}
|
2044 |
if ($this->is_pw_sequential_codepoints($pw)) {
|
2045 |
if ($errors !== null) {
|
2046 |
$errors->add(self::ID,
|
2047 |
+
$this->err(__("Passwords can't have that many sequential characters.", self::ID)),
|
2048 |
array('form-field' => 'pass1')
|
2049 |
);
|
2050 |
}
|
2060 |
{
|
2061 |
if ($errors !== null) {
|
2062 |
$errors->add(self::ID,
|
2063 |
+
$this->err(__("Passwords can't contain user data.", self::ID)),
|
2064 |
array('form-field' => 'pass1')
|
2065 |
);
|
2066 |
}
|
2071 |
{
|
2072 |
if ($errors !== null) {
|
2073 |
$errors->add(self::ID,
|
2074 |
+
$this->err(__("Passwords can't contain site info.", self::ID)),
|
2075 |
array('form-field' => 'pass1')
|
2076 |
);
|
2077 |
}
|
2080 |
if ($all_tests && $this->is_pw_dictionary($pw)) {
|
2081 |
if ($errors !== null) {
|
2082 |
$errors->add(self::ID,
|
2083 |
+
$this->err(__("Password is too common.", self::ID)),
|
2084 |
array('form-field' => 'pass1')
|
2085 |
);
|
2086 |
}
|
2089 |
if ($this->is_pw_dict_program($stripped)) {
|
2090 |
if ($errors !== null) {
|
2091 |
$errors->add(self::ID,
|
2092 |
+
$this->err(__("Passwords can't be variations of dictionary words.", self::ID)),
|
2093 |
array('form-field' => 'pass1')
|
2094 |
);
|
2095 |
}
|
readme.txt
CHANGED
@@ -63,6 +63,7 @@ The tests have caught every password dictionary entry I've tried.
|
|
63 |
|
64 |
= Improvements Over Similar WordPress Plugins =
|
65 |
|
|
|
66 |
* The plugin itself is secure against SQL, HTML, and header injections
|
67 |
* Notice-free code means no information disclosures if `display_errors`
|
68 |
is on and `error_reporting` includes `E_NOTICE`
|
@@ -159,11 +160,15 @@ clients and friends.
|
|
159 |
1. Upload the `login-security-solution` directory to your
|
160 |
server's `/wp-content/plugins/` directory
|
161 |
|
162 |
-
1. Activate the plugin
|
|
|
|
|
163 |
|
164 |
1. Adjust the settings as desired. This plugin's settings page can be
|
165 |
reached via a sub-menu entry under WordPress' "Settings" menu or this
|
166 |
-
plugin's entry on WordPress' "Plugins" page.
|
|
|
|
|
167 |
|
168 |
1. Run the "Change All Passwords" process. This is necessary to ensure
|
169 |
all of your users have strong passwords. The user interface for
|
@@ -177,9 +182,7 @@ clients and friends.
|
|
177 |
|
178 |
A thorough set of unit tests are found in the `tests` directory.
|
179 |
|
180 |
-
The plugin needs to be
|
181 |
-
a working WordPress installation. The plugin does not need to be
|
182 |
-
activated for the tests to run.
|
183 |
|
184 |
To execute the tests, `cd` into this plugin's directory and
|
185 |
call `phpunit tests` .
|
@@ -204,11 +207,29 @@ are not using the `InnoDB` storage engine.
|
|
204 |
|
205 |
== Frequently Asked Questions ==
|
206 |
|
207 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
|
209 |
|
210 |
== Changelog ==
|
211 |
|
|
|
|
|
|
|
|
|
|
|
212 |
= 0.3.0 =
|
213 |
* Use UTF-8 encoding for `htmlspecialchars()` instead of `DB_CHARSET`.
|
214 |
* Tested under WordPress 3.3.1.
|
63 |
|
64 |
= Improvements Over Similar WordPress Plugins =
|
65 |
|
66 |
+
* Multisite network support
|
67 |
* The plugin itself is secure against SQL, HTML, and header injections
|
68 |
* Notice-free code means no information disclosures if `display_errors`
|
69 |
is on and `error_reporting` includes `E_NOTICE`
|
160 |
1. Upload the `login-security-solution` directory to your
|
161 |
server's `/wp-content/plugins/` directory
|
162 |
|
163 |
+
1. Activate the plugin using WordPress' admin interface:
|
164 |
+
* Regular sites: Plugins
|
165 |
+
* Sites using multisite networks: My Sites | Network Admin | Plugins
|
166 |
|
167 |
1. Adjust the settings as desired. This plugin's settings page can be
|
168 |
reached via a sub-menu entry under WordPress' "Settings" menu or this
|
169 |
+
plugin's entry on WordPress' "Plugins" page. Sites using WordPress'
|
170 |
+
multisite network capabilitiy will find the "Settings" and "Plugin"
|
171 |
+
menus under "My Sites | Network Admin".
|
172 |
|
173 |
1. Run the "Change All Passwords" process. This is necessary to ensure
|
174 |
all of your users have strong passwords. The user interface for
|
182 |
|
183 |
A thorough set of unit tests are found in the `tests` directory.
|
184 |
|
185 |
+
The plugin needs to be installed and activated before running the tests.
|
|
|
|
|
186 |
|
187 |
To execute the tests, `cd` into this plugin's directory and
|
188 |
call `phpunit tests` .
|
207 |
|
208 |
== Frequently Asked Questions ==
|
209 |
|
210 |
+
= Where did the "Change All Passwords" interface go? =
|
211 |
+
|
212 |
+
A link to the page is found in this plugin's entry in the "Plugins" admin
|
213 |
+
interface:
|
214 |
+
* Regular sites: Plugins
|
215 |
+
* Sites using multisite networks: My Sites | Network Admin | Plugins
|
216 |
+
|
217 |
+
= How do I generate the POT translation file? =
|
218 |
+
|
219 |
+
Get the translation tools from `http://i18n.svn.wordpress.org/tools/trunk/`
|
220 |
+
then `cd` into that directory and run:
|
221 |
+
|
222 |
+
php makepot.php wp-plugin ../login-security-solution \
|
223 |
+
../login-security-solution/languages/login-security-solution.pot
|
224 |
|
225 |
|
226 |
== Changelog ==
|
227 |
|
228 |
+
= 0.4.0 =
|
229 |
+
* Add multisite network support.
|
230 |
+
* Keep unit tests from deleting settings. Note: removes the ability to
|
231 |
+
run the unit tests without activating the plugin.
|
232 |
+
|
233 |
= 0.3.0 =
|
234 |
* Use UTF-8 encoding for `htmlspecialchars()` instead of `DB_CHARSET`.
|
235 |
* Tested under WordPress 3.3.1.
|
tests/Accessor.php
CHANGED
@@ -32,12 +32,6 @@ unset($GLOBALS['login_security_solution']);
|
|
32 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2
|
33 |
*/
|
34 |
class Accessor extends login_security_solution_admin {
|
35 |
-
/**
|
36 |
-
* This plugin's table name prefix FOR TESTING
|
37 |
-
* @var string
|
38 |
-
*/
|
39 |
-
protected $prefix = 'login_security_solution__tests__';
|
40 |
-
|
41 |
/**
|
42 |
* Is this class being used by our unit tests?
|
43 |
* @var bool
|
32 |
* @license http://www.gnu.org/licenses/gpl-2.0.html GPLv2
|
33 |
*/
|
34 |
class Accessor extends login_security_solution_admin {
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
/**
|
36 |
* Is this class being used by our unit tests?
|
37 |
* @var bool
|
tests/PasswordValidationTest.php
CHANGED
@@ -56,6 +56,11 @@ class PasswordValidationTest extends TestCase {
|
|
56 |
}
|
57 |
|
58 |
|
|
|
|
|
|
|
|
|
|
|
59 |
public function test_is_pw_dictionary__grepavail() {
|
60 |
if (!self::$dict_available) {
|
61 |
$this->markTestSkipped('grep not available');
|
@@ -437,7 +442,7 @@ class PasswordValidationTest extends TestCase {
|
|
437 |
$this->assertFalse($actual,
|
438 |
"password not being set should have failed.");
|
439 |
$this->assertEquals(
|
440 |
-
|
441 |
$errors->get_error_message()
|
442 |
);
|
443 |
}
|
@@ -448,7 +453,7 @@ class PasswordValidationTest extends TestCase {
|
|
448 |
$this->assertFalse($actual,
|
449 |
"'array('abc')' should have failed.");
|
450 |
$this->assertEquals(
|
451 |
-
|
452 |
$errors->get_error_message()
|
453 |
);
|
454 |
}
|
@@ -461,7 +466,7 @@ class PasswordValidationTest extends TestCase {
|
|
461 |
$this->assertFalse($actual,
|
462 |
"'" . $this->user->user_pass . "' should have failed.");
|
463 |
$this->assertEquals(
|
464 |
-
|
465 |
$errors->get_error_message()
|
466 |
);
|
467 |
}
|
@@ -478,7 +483,7 @@ class PasswordValidationTest extends TestCase {
|
|
478 |
$this->assertFalse($actual,
|
479 |
"'" . $this->user->user_pass . "' should have failed.");
|
480 |
$this->assertEquals(
|
481 |
-
|
482 |
$errors->get_error_message()
|
483 |
);
|
484 |
}
|
@@ -492,7 +497,7 @@ class PasswordValidationTest extends TestCase {
|
|
492 |
$this->assertFalse($actual,
|
493 |
"'" . $this->user->user_pass . "' should have failed.");
|
494 |
$this->assertEquals(
|
495 |
-
|
496 |
$errors->get_error_message()
|
497 |
);
|
498 |
}
|
@@ -505,7 +510,7 @@ class PasswordValidationTest extends TestCase {
|
|
505 |
$this->assertFalse($actual,
|
506 |
"'" . $this->user->user_pass . "' should have failed.");
|
507 |
$this->assertEquals(
|
508 |
-
sprintf(
|
509 |
$errors->get_error_message()
|
510 |
);
|
511 |
}
|
@@ -518,7 +523,7 @@ class PasswordValidationTest extends TestCase {
|
|
518 |
$this->assertFalse($actual,
|
519 |
"'" . $this->user->user_pass . "' should have failed.");
|
520 |
$this->assertEquals(
|
521 |
-
sprintf(
|
522 |
$errors->get_error_message()
|
523 |
);
|
524 |
}
|
@@ -531,7 +536,7 @@ class PasswordValidationTest extends TestCase {
|
|
531 |
$this->assertFalse($actual,
|
532 |
"'" . $this->user->user_pass . "' should have failed.");
|
533 |
$this->assertEquals(
|
534 |
-
sprintf(
|
535 |
$errors->get_error_message()
|
536 |
);
|
537 |
}
|
@@ -544,7 +549,7 @@ class PasswordValidationTest extends TestCase {
|
|
544 |
$this->assertFalse($actual,
|
545 |
"'" . $this->user->user_pass . "' should have failed.");
|
546 |
$this->assertEquals(
|
547 |
-
|
548 |
$errors->get_error_message()
|
549 |
);
|
550 |
}
|
@@ -557,7 +562,7 @@ class PasswordValidationTest extends TestCase {
|
|
557 |
$this->assertFalse($actual,
|
558 |
"'" . $this->user->user_pass . "' should have failed.");
|
559 |
$this->assertEquals(
|
560 |
-
|
561 |
$errors->get_error_message()
|
562 |
);
|
563 |
}
|
@@ -570,7 +575,7 @@ class PasswordValidationTest extends TestCase {
|
|
570 |
$this->assertFalse($actual,
|
571 |
"'" . $this->user->user_pass . "' should have failed.");
|
572 |
$this->assertEquals(
|
573 |
-
|
574 |
$errors->get_error_message()
|
575 |
);
|
576 |
}
|
@@ -583,7 +588,7 @@ class PasswordValidationTest extends TestCase {
|
|
583 |
$this->assertFalse($actual,
|
584 |
"'" . $this->user->user_pass . "' should have failed.");
|
585 |
$this->assertEquals(
|
586 |
-
|
587 |
$errors->get_error_message()
|
588 |
);
|
589 |
}
|
@@ -596,7 +601,7 @@ class PasswordValidationTest extends TestCase {
|
|
596 |
$this->assertFalse($actual,
|
597 |
"'" . $this->user->user_pass . "' should have failed.");
|
598 |
$this->assertEquals(
|
599 |
-
|
600 |
$errors->get_error_message()
|
601 |
);
|
602 |
}
|
@@ -609,7 +614,7 @@ class PasswordValidationTest extends TestCase {
|
|
609 |
$this->assertFalse($actual,
|
610 |
"'" . $this->user->user_pass . "' should have failed.");
|
611 |
$this->assertEquals(
|
612 |
-
|
613 |
$errors->get_error_message()
|
614 |
);
|
615 |
}
|
@@ -625,7 +630,7 @@ class PasswordValidationTest extends TestCase {
|
|
625 |
$this->assertFalse($actual,
|
626 |
"'" . $this->user->user_pass . "' should have failed.");
|
627 |
$this->assertEquals(
|
628 |
-
|
629 |
$errors->get_error_message()
|
630 |
);
|
631 |
}
|
56 |
}
|
57 |
|
58 |
|
59 |
+
protected function err($message) {
|
60 |
+
return self::$lss->err($message);
|
61 |
+
}
|
62 |
+
|
63 |
+
|
64 |
public function test_is_pw_dictionary__grepavail() {
|
65 |
if (!self::$dict_available) {
|
66 |
$this->markTestSkipped('grep not available');
|
442 |
$this->assertFalse($actual,
|
443 |
"password not being set should have failed.");
|
444 |
$this->assertEquals(
|
445 |
+
$this->err("Password not set."),
|
446 |
$errors->get_error_message()
|
447 |
);
|
448 |
}
|
453 |
$this->assertFalse($actual,
|
454 |
"'array('abc')' should have failed.");
|
455 |
$this->assertEquals(
|
456 |
+
$this->err("Passwords must be strings."),
|
457 |
$errors->get_error_message()
|
458 |
);
|
459 |
}
|
466 |
$this->assertFalse($actual,
|
467 |
"'" . $this->user->user_pass . "' should have failed.");
|
468 |
$this->assertEquals(
|
469 |
+
$this->err("Passwords must use ASCII characters."),
|
470 |
$errors->get_error_message()
|
471 |
);
|
472 |
}
|
483 |
$this->assertFalse($actual,
|
484 |
"'" . $this->user->user_pass . "' should have failed.");
|
485 |
$this->assertEquals(
|
486 |
+
$this->err("Password is too short."),
|
487 |
$errors->get_error_message()
|
488 |
);
|
489 |
}
|
497 |
$this->assertFalse($actual,
|
498 |
"'" . $this->user->user_pass . "' should have failed.");
|
499 |
$this->assertEquals(
|
500 |
+
$this->err("Password is too short."),
|
501 |
$errors->get_error_message()
|
502 |
);
|
503 |
}
|
510 |
$this->assertFalse($actual,
|
511 |
"'" . $this->user->user_pass . "' should have failed.");
|
512 |
$this->assertEquals(
|
513 |
+
$this->err(sprintf("Passwords must either contain punctuation marks / symbols or be %d characters long.", self::$lss->options['pw_complexity_exemption_length'])),
|
514 |
$errors->get_error_message()
|
515 |
);
|
516 |
}
|
523 |
$this->assertFalse($actual,
|
524 |
"'" . $this->user->user_pass . "' should have failed.");
|
525 |
$this->assertEquals(
|
526 |
+
$this->err(sprintf("Passwords must either contain numbers or be %d characters long.", self::$lss->options['pw_complexity_exemption_length'])),
|
527 |
$errors->get_error_message()
|
528 |
);
|
529 |
}
|
536 |
$this->assertFalse($actual,
|
537 |
"'" . $this->user->user_pass . "' should have failed.");
|
538 |
$this->assertEquals(
|
539 |
+
$this->err(sprintf("Passwords must either contain upper-case and lower-case letters or be %d characters long.", self::$lss->options['pw_complexity_exemption_length'])),
|
540 |
$errors->get_error_message()
|
541 |
);
|
542 |
}
|
549 |
$this->assertFalse($actual,
|
550 |
"'" . $this->user->user_pass . "' should have failed.");
|
551 |
$this->assertEquals(
|
552 |
+
$this->err("Passwords can't be sequential keys."),
|
553 |
$errors->get_error_message()
|
554 |
);
|
555 |
}
|
562 |
$this->assertFalse($actual,
|
563 |
"'" . $this->user->user_pass . "' should have failed.");
|
564 |
$this->assertEquals(
|
565 |
+
$this->err("Passwords can't have that many sequential characters."),
|
566 |
$errors->get_error_message()
|
567 |
);
|
568 |
}
|
575 |
$this->assertFalse($actual,
|
576 |
"'" . $this->user->user_pass . "' should have failed.");
|
577 |
$this->assertEquals(
|
578 |
+
$this->err("Passwords can't contain user data."),
|
579 |
$errors->get_error_message()
|
580 |
);
|
581 |
}
|
588 |
$this->assertFalse($actual,
|
589 |
"'" . $this->user->user_pass . "' should have failed.");
|
590 |
$this->assertEquals(
|
591 |
+
$this->err("Passwords can't contain user data."),
|
592 |
$errors->get_error_message()
|
593 |
);
|
594 |
}
|
601 |
$this->assertFalse($actual,
|
602 |
"'" . $this->user->user_pass . "' should have failed.");
|
603 |
$this->assertEquals(
|
604 |
+
$this->err("Passwords can't contain site info."),
|
605 |
$errors->get_error_message()
|
606 |
);
|
607 |
}
|
614 |
$this->assertFalse($actual,
|
615 |
"'" . $this->user->user_pass . "' should have failed.");
|
616 |
$this->assertEquals(
|
617 |
+
$this->err("Password is too common."),
|
618 |
$errors->get_error_message()
|
619 |
);
|
620 |
}
|
630 |
$this->assertFalse($actual,
|
631 |
"'" . $this->user->user_pass . "' should have failed.");
|
632 |
$this->assertEquals(
|
633 |
+
$this->err("Passwords can't be variations of dictionary words."),
|
634 |
$errors->get_error_message()
|
635 |
);
|
636 |
}
|
tests/TestCase.php
CHANGED
@@ -21,6 +21,21 @@
|
|
21 |
*/
|
22 |
global $wp_rewrite;
|
23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
/**
|
25 |
* Overrides the wp_mail() function so we can ensure the messages are
|
26 |
* composed when and how they should be
|
@@ -51,6 +66,13 @@ if (!is_readable($wp_load)) {
|
|
51 |
}
|
52 |
require_once $wp_load;
|
53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
/**
|
55 |
* Get the class we will use for testing
|
56 |
*/
|
@@ -170,8 +192,8 @@ abstract class TestCase extends PHPUnit_Framework_TestCase {
|
|
170 |
|
171 |
if (self::$db_needed && self::are_transactions_available()) {
|
172 |
self::$db_has_transactions = true;
|
173 |
-
self::$lss->activate();
|
174 |
$wpdb->query('START TRANSACTION');
|
|
|
175 |
} else {
|
176 |
self::$db_has_transactions = false;
|
177 |
}
|
@@ -185,7 +207,6 @@ abstract class TestCase extends PHPUnit_Framework_TestCase {
|
|
185 |
|
186 |
if (self::$db_has_transactions) {
|
187 |
$wpdb->query('ROLLBACK');
|
188 |
-
self::$lss->deactivate();
|
189 |
}
|
190 |
|
191 |
self::$lss = null;
|
21 |
*/
|
22 |
global $wp_rewrite;
|
23 |
|
24 |
+
|
25 |
+
/*
|
26 |
+
* Hacks to keep WordPress multisite network mode happy under PHPUnit.
|
27 |
+
*/
|
28 |
+
|
29 |
+
// Undefined index: HTTP_HOST in wp-includes/ms-settings.php.
|
30 |
+
$_SERVER['HTTP_HOST'] = 'localhost';
|
31 |
+
|
32 |
+
// Undefined variable: wpdb in wp-includes/ms-settings.php.
|
33 |
+
global $wpdb;
|
34 |
+
|
35 |
+
// Trying to get property of non-object in wp-includes/functions.php.
|
36 |
+
global $current_site, $current_blog;
|
37 |
+
|
38 |
+
|
39 |
/**
|
40 |
* Overrides the wp_mail() function so we can ensure the messages are
|
41 |
* composed when and how they should be
|
66 |
}
|
67 |
require_once $wp_load;
|
68 |
|
69 |
+
|
70 |
+
if (is_multisite()) {
|
71 |
+
// Workaround for the authentication check in my activate() method.
|
72 |
+
define('WP_NETWORK_ADMIN', true);
|
73 |
+
}
|
74 |
+
|
75 |
+
|
76 |
/**
|
77 |
* Get the class we will use for testing
|
78 |
*/
|
192 |
|
193 |
if (self::$db_needed && self::are_transactions_available()) {
|
194 |
self::$db_has_transactions = true;
|
|
|
195 |
$wpdb->query('START TRANSACTION');
|
196 |
+
$wpdb->query('DELETE FROM `' . self::$lss->table_fail . '`');
|
197 |
} else {
|
198 |
self::$db_has_transactions = false;
|
199 |
}
|
207 |
|
208 |
if (self::$db_has_transactions) {
|
209 |
$wpdb->query('ROLLBACK');
|
|
|
210 |
}
|
211 |
|
212 |
self::$lss = null;
|