Version Description
- Enhancement: Add UI to cancel in progress File Scan.
- Enhancement: Add basic admin debug page to help diagnosing and resolving issues. Particularly with the events.
- Enhancement: Add debug settings JSON editor.
- Enhancement: Continually evaluate password strength for users instead of only during registration.
- Enhancement: Introduce Password Requirements module for managing and enforcing password requirements.
- Bug Fix: Accessing password requirement settings would not resolve properly in some instances.
- Bug Fix: Away Mode would not lock out users who were already logged-in during the "away" period.
- Bug Fix: Enforce the Strong Passwords requirement during Security Check.
- Bug Fix: Ensure scheduling lock is cleared by the Cron Scheduler when not proceeding with running events.
- Bug Fix: If a password requirement has been disabled or is no longer available, don't consider the password as needing a change.
- Bug Fix: Only hide "Acknowledge Weak Password" checkbox if the user was not allowed to use a weak password.
- Bug Fix: Password strength would not be evaluated if password was set using custom PHP or CLI commands.
- Bug Fix: Prevent File Change from getting stuck in an infinite rescheduling loop on the first step.
- Bug Fix: Remove distributed storage table on uninstall.
- Tweak: Don't write to the tracked files setting if the file hash has not changed.
- Tweak: If no last password change date is recorded for the user, treat their registration date as the last change date.
Download this release
Release Info
Developer | chrisjean |
Plugin | iThemes Security (formerly Better WP Security) |
Version | 7.0.2 |
Comparing to | |
See all releases |
Code changes from version 7.0.1 to 7.0.2
- better-wp-security.php +1 -1
- core/admin-pages/css/style.css +16 -0
- core/admin-pages/init.php +6 -1
- core/admin-pages/js/debug.js +86 -0
- core/admin-pages/js/util.js +37 -1
- core/admin-pages/page-debug.php +425 -0
- core/admin-pages/page-logs.php +2 -0
- core/admin-pages/page-settings.php +2 -0
- core/core.php +7 -9
- core/history.txt +22 -1
- core/lib.php +33 -3
- core/lib/class-itsec-lib-canonical-roles.php +43 -0
- core/lib/class-itsec-lib-password-requirements.php +156 -159
- core/lib/class-itsec-mail.php +110 -26
- core/lib/class-itsec-scheduler-cron.php +4 -0
- core/lib/form.php +18 -0
- core/lib/mail-templates/header.html +1 -0
- core/lib/schema.php +1 -0
- core/lib/validator.php +31 -1
- core/lockout.php +0 -3
- core/modules/away-mode/class-itsec-away-mode.php +6 -1
- core/modules/file-change/class-itsec-file-change.php +1 -1
- core/modules/file-change/css/settings.css +14 -0
- core/modules/file-change/js/settings-page.js +15 -0
- core/modules/file-change/logs.php +11 -1
- core/modules/file-change/scanner.php +18 -8
- core/modules/file-change/settings-page.php +15 -4
- core/modules/notification-center/class-notification-center.php +4 -2
- core/modules/notification-center/debug.php +92 -0
- core/modules/notification-center/js/debug.js +22 -0
- core/modules/password-requirements/active.php +6 -0
- core/modules/password-requirements/class-itsec-password-requirements.php +480 -0
- core/modules/password-requirements/css/index.php +1 -0
- core/modules/password-requirements/css/settings-page.css +3 -0
- core/modules/password-requirements/index.php +1 -0
- core/modules/password-requirements/js/index.php +1 -0
- core/modules/password-requirements/js/settings-page.js +24 -0
- core/modules/password-requirements/settings-page.php +115 -0
- core/modules/password-requirements/settings.php +47 -0
- core/modules/password-requirements/validator.php +62 -0
- core/modules/pro/settings-page.php +0 -16
- core/modules/security-check/scanner.php +16 -1
- core/modules/strong-passwords/class-itsec-strong-passwords.php +139 -168
- core/modules/strong-passwords/js/script.js +1 -8
- core/modules/strong-passwords/settings-page.php +0 -51
- core/modules/strong-passwords/setup.php +26 -4
- core/notify.php +10 -5
- history.txt +17 -0
- readme.txt +21 -3
better-wp-security.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
* Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
|
7 |
* Author: iThemes
|
8 |
* Author URI: https://ithemes.com
|
9 |
-
* Version: 7.0.
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
6 |
* Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
|
7 |
* Author: iThemes
|
8 |
* Author URI: https://ithemes.com
|
9 |
+
* Version: 7.0.2
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
core/admin-pages/css/style.css
CHANGED
@@ -851,3 +851,19 @@ body.security_page_itsec-logs #old-logs-migration-status img {
|
|
851 |
body.security_page_itsec-logs #old-logs-migration-status p {
|
852 |
line-height: 1.4em;
|
853 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
851 |
body.security_page_itsec-logs #old-logs-migration-status p {
|
852 |
line-height: 1.4em;
|
853 |
}
|
854 |
+
#itsec-system-info, #itsec-settings-editor {
|
855 |
+
font-family: monospace;
|
856 |
+
width: 100%;
|
857 |
+
margin: 0;
|
858 |
+
height: 300px;
|
859 |
+
padding: 20px;
|
860 |
+
border-radius: 0;
|
861 |
+
resize: none;
|
862 |
+
font-size: 12px;
|
863 |
+
line-height: 20px;
|
864 |
+
outline: 0;
|
865 |
+
}
|
866 |
+
|
867 |
+
#itsec-settings-editor:empty {
|
868 |
+
display: none;
|
869 |
+
}
|
core/admin-pages/init.php
CHANGED
@@ -16,6 +16,7 @@ final class ITSEC_Admin_Page_Loader {
|
|
16 |
add_action( 'wp_ajax_itsec_settings_page', array( $this, 'handle_ajax_request' ) );
|
17 |
add_action( 'wp_ajax_itsec_logs_page', array( $this, 'handle_ajax_request' ) );
|
18 |
add_action( 'wp_ajax_itsec_help_page', array( $this, 'handle_ajax_request' ) );
|
|
|
19 |
add_action( 'wp_ajax_itsec-set-user-setting', array( $this, 'handle_user_setting' ) );
|
20 |
|
21 |
// Filters for validating user settings
|
@@ -23,7 +24,7 @@ final class ITSEC_Admin_Page_Loader {
|
|
23 |
}
|
24 |
|
25 |
public function add_scripts() {
|
26 |
-
|
27 |
}
|
28 |
|
29 |
public function add_styles() {
|
@@ -46,6 +47,10 @@ final class ITSEC_Admin_Page_Loader {
|
|
46 |
$page_refs[] = add_submenu_page( 'itsec', '', '<span style="color:#2EA2CC">' . __( 'Go Pro', 'better-wp-security' ) . '</span>', $capability, 'itsec-go-pro', array( $this, 'show_page' ) );
|
47 |
}
|
48 |
|
|
|
|
|
|
|
|
|
49 |
foreach ( $page_refs as $page_ref ) {
|
50 |
add_action( "load-$page_ref", array( $this, 'load' ) );
|
51 |
}
|
16 |
add_action( 'wp_ajax_itsec_settings_page', array( $this, 'handle_ajax_request' ) );
|
17 |
add_action( 'wp_ajax_itsec_logs_page', array( $this, 'handle_ajax_request' ) );
|
18 |
add_action( 'wp_ajax_itsec_help_page', array( $this, 'handle_ajax_request' ) );
|
19 |
+
add_action( 'wp_ajax_itsec_debug_page', array( $this, 'handle_ajax_request' ) );
|
20 |
add_action( 'wp_ajax_itsec-set-user-setting', array( $this, 'handle_user_setting' ) );
|
21 |
|
22 |
// Filters for validating user settings
|
24 |
}
|
25 |
|
26 |
public function add_scripts() {
|
27 |
+
|
28 |
}
|
29 |
|
30 |
public function add_styles() {
|
47 |
$page_refs[] = add_submenu_page( 'itsec', '', '<span style="color:#2EA2CC">' . __( 'Go Pro', 'better-wp-security' ) . '</span>', $capability, 'itsec-go-pro', array( $this, 'show_page' ) );
|
48 |
}
|
49 |
|
50 |
+
if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG ) {
|
51 |
+
$page_refs[] = add_submenu_page( 'itsec', __( 'iThemes Security Debug', 'better-wp-security' ), __( 'Debug' ), $capability, 'itsec-debug', array( $this, 'show_page' ) );
|
52 |
+
}
|
53 |
+
|
54 |
foreach ( $page_refs as $page_ref ) {
|
55 |
add_action( "load-$page_ref", array( $this, 'load' ) );
|
56 |
}
|
core/admin-pages/js/debug.js
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function ( $, itsecUtil, codeEditor ) {
|
2 |
+
$( function () {
|
3 |
+
var $messages = $( "#itsec-messages" );
|
4 |
+
|
5 |
+
$( '#itsec-scheduler-events' ).on( 'click', '.button', function () {
|
6 |
+
|
7 |
+
var $btn = $( this );
|
8 |
+
$btn.prop( 'disabled', true );
|
9 |
+
|
10 |
+
// We are purposely using attr() so as not to parse the data string as json.
|
11 |
+
itsecUtil.sendAJAXRequest( '', 'run_event', { id: $btn.data( 'id' ), data: $btn.attr( 'data-data' ) }, function ( result ) {
|
12 |
+
|
13 |
+
$btn.prop( 'disabled', false );
|
14 |
+
|
15 |
+
if ( result.success ) {
|
16 |
+
$( 'table', '#itsec-scheduler-events' ).replaceWith( result.response );
|
17 |
+
}
|
18 |
+
|
19 |
+
itsecUtil.displayNotices( result, $messages );
|
20 |
+
} );
|
21 |
+
} );
|
22 |
+
|
23 |
+
$( '#itsec-scheduler-reset' ).on( 'click', function () {
|
24 |
+
|
25 |
+
var $btn = $( this );
|
26 |
+
$btn.prop( 'disabled', true );
|
27 |
+
|
28 |
+
itsecUtil.sendAJAXRequest( '', 'reset_scheduler', {}, function ( result ) {
|
29 |
+
|
30 |
+
$btn.prop( 'disabled', false );
|
31 |
+
|
32 |
+
if ( result.success ) {
|
33 |
+
$( 'table', '#itsec-scheduler-events' ).replaceWith( result.response );
|
34 |
+
}
|
35 |
+
|
36 |
+
itsecUtil.displayNotices( result, $messages );
|
37 |
+
} );
|
38 |
+
} );
|
39 |
+
|
40 |
+
var $saveBtn = $( '#itsec-settings-save' ), $loadBtn = $( "#itsec-settings-load" );
|
41 |
+
|
42 |
+
$loadBtn.on( 'click', function () {
|
43 |
+
$loadBtn.prop( 'disabled', true );
|
44 |
+
|
45 |
+
itsecUtil.sendAJAXRequest( $( '#itsec-settings-module' ).val(), 'load_settings', {}, function ( result ) {
|
46 |
+
itsecUtil.displayNotices( result, $messages );
|
47 |
+
|
48 |
+
$loadBtn.prop( 'disabled', false );
|
49 |
+
$saveBtn.prop( 'disabled', false );
|
50 |
+
setEditorContent( JSON.stringify( result.response, null, 4 ) );
|
51 |
+
} );
|
52 |
+
} );
|
53 |
+
|
54 |
+
$saveBtn.on( 'click', function () {
|
55 |
+
|
56 |
+
$loadBtn.prop( 'disabled', true );
|
57 |
+
$saveBtn.prop( 'disabled', true );
|
58 |
+
|
59 |
+
itsecUtil.sendAJAXRequest( $( '#itsec-settings-module' ).val(), 'save_settings', getEditorContent(), function ( result ) {
|
60 |
+
itsecUtil.displayNotices( result, $messages );
|
61 |
+
|
62 |
+
$loadBtn.prop( 'disabled', false );
|
63 |
+
$saveBtn.prop( 'disabled', false );
|
64 |
+
|
65 |
+
if ( result.success ) {
|
66 |
+
setEditorContent( JSON.stringify( result.response, null, 4 ) );
|
67 |
+
}
|
68 |
+
} );
|
69 |
+
} );
|
70 |
+
|
71 |
+
var $editor = $( "#itsec-settings-editor" ), editor;
|
72 |
+
|
73 |
+
function setEditorContent( content ) {
|
74 |
+
if ( codeEditor ) {
|
75 |
+
if ( !editor ) editor = codeEditor.initialize( $editor );
|
76 |
+
editor.codemirror.setValue( content );
|
77 |
+
} else {
|
78 |
+
$editor.val( content );
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
function getEditorContent() {
|
83 |
+
return editor ? editor.codemirror.getValue() : $editor.val();
|
84 |
+
}
|
85 |
+
} );
|
86 |
+
})( jQuery, window.itsecUtil, wp.codeEditor );
|
core/admin-pages/js/util.js
CHANGED
@@ -166,6 +166,42 @@ var itsecUtil = {
|
|
166 |
}
|
167 |
// If the requested parameter doesn't exist, return false
|
168 |
return false;
|
169 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
};
|
166 |
}
|
167 |
// If the requested parameter doesn't exist, return false
|
168 |
return false;
|
169 |
+
},
|
170 |
+
|
171 |
+
buildNotices: function ( response, asAlt ) {
|
172 |
+
var notices = [],
|
173 |
+
types = ['error', 'warning', 'message', 'info'];
|
174 |
+
|
175 |
+
for ( var i = 0; i < types.length; i++ ) {
|
176 |
+
for ( var j = 0; j < response[types[i] + 's'].length; j++ ) {
|
177 |
+
notices.push( itsecUtil.makeNotice( response[types[i] + 's'][j], types[i], asAlt ) );
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
return notices;
|
182 |
+
},
|
183 |
+
|
184 |
+
makeNotice: function ( message, type, asAlt ) {
|
185 |
+
type = type === 'message' ? 'success' : type;
|
186 |
|
187 |
+
var className = 'notice notice-' + type;
|
188 |
+
|
189 |
+
if ( asAlt ) {
|
190 |
+
className += ' notice-alt';
|
191 |
+
}
|
192 |
+
|
193 |
+
return jQuery( '<div>', { class: className } )
|
194 |
+
.append( jQuery( '<p>', { html: message } ) );
|
195 |
+
},
|
196 |
+
|
197 |
+
displayNotices: function ( response, $container, asAlt ) {
|
198 |
+
var notices = itsecUtil.buildNotices( response, asAlt );
|
199 |
+
|
200 |
+
for ( var i = 0; i < notices.length; i++ ) {
|
201 |
+
(function ( $notice ) {
|
202 |
+
$container.append( $notice );
|
203 |
+
setTimeout( function () {$notice.remove();}, 10000 );
|
204 |
+
})( notices[i].clone() );
|
205 |
+
}
|
206 |
+
},
|
207 |
};
|
core/admin-pages/page-debug.php
ADDED
@@ -0,0 +1,425 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Debug_Page {
|
4 |
+
|
5 |
+
/** @var string */
|
6 |
+
private $self_url;
|
7 |
+
|
8 |
+
public function __construct() {
|
9 |
+
add_action( 'itsec-page-show', array( $this, 'handle_page_load' ) );
|
10 |
+
add_action( 'itsec-page-ajax', array( $this, 'handle_ajax_request' ) );
|
11 |
+
add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
|
12 |
+
add_action( 'admin_print_styles', array( $this, 'add_styles' ) );
|
13 |
+
|
14 |
+
ITSEC_Modules::load_module_file( 'debug.php', ':active' );
|
15 |
+
}
|
16 |
+
|
17 |
+
public function handle_page_load( $self_url ) {
|
18 |
+
$this->self_url = $self_url;
|
19 |
+
|
20 |
+
$this->show_settings_page();
|
21 |
+
}
|
22 |
+
|
23 |
+
public function add_scripts() {
|
24 |
+
|
25 |
+
$deps = array( 'itsec-util' );
|
26 |
+
|
27 |
+
if ( function_exists( 'wp_enqueue_code_editor' ) ) {
|
28 |
+
$deps[] = 'code-editor';
|
29 |
+
wp_enqueue_code_editor( array(
|
30 |
+
'type' => 'application/json'
|
31 |
+
) );
|
32 |
+
}
|
33 |
+
|
34 |
+
ITSEC_Lib::enqueue_util( array( 'action' => 'itsec_debug_page', 'nonce' => 'itsec-debug-page' ) );
|
35 |
+
wp_enqueue_script( 'itsec-debug', plugins_url( 'js/debug.js', __FILE__ ), $deps, ITSEC_Core::get_plugin_build() );
|
36 |
+
|
37 |
+
do_action( 'itsec_debug_page_enqueue' );
|
38 |
+
}
|
39 |
+
|
40 |
+
public function add_styles() {
|
41 |
+
wp_enqueue_style( 'itsec-debug-page-style', plugins_url( 'css/style.css', __FILE__ ), array(), ITSEC_Core::get_plugin_build() );
|
42 |
+
}
|
43 |
+
|
44 |
+
public function handle_ajax_request() {
|
45 |
+
if ( WP_DEBUG ) {
|
46 |
+
ini_set( 'display_errors', 1 );
|
47 |
+
}
|
48 |
+
|
49 |
+
ITSEC_Core::set_interactive( true );
|
50 |
+
|
51 |
+
$method = ( isset( $_POST['method'] ) && is_string( $_POST['method'] ) ) ? $_POST['method'] : '';
|
52 |
+
$module = ( isset( $_POST['module'] ) && is_string( $_POST['module'] ) ) ? $_POST['module'] : '';
|
53 |
+
|
54 |
+
if ( empty( $GLOBALS['hook_suffix'] ) ) {
|
55 |
+
$GLOBALS['hook_suffix'] = 'security_page_itsec-debug';
|
56 |
+
}
|
57 |
+
|
58 |
+
if ( false === check_ajax_referer( 'itsec-debug-page', 'nonce', false ) ) {
|
59 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-failed-nonce', __( 'A nonce security check failed, preventing the request from completing as expected. Please try reloading the page and trying again.', 'better-wp-security' ) ) );
|
60 |
+
} elseif ( ! ITSEC_Core::current_user_can_manage() ) {
|
61 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-insufficient-privileges', __( 'A permissions security check failed, preventing the request from completing as expected. The currently logged in user does not have sufficient permissions to make this request. Please try reloading the page and trying again.', 'better-wp-security' ) ) );
|
62 |
+
} elseif ( empty( $method ) ) {
|
63 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-missing-method', __( 'The server did not receive a valid request. The required "method" argument is missing. Please try again.', 'better-wp-security' ) ) );
|
64 |
+
} elseif ( 'handle_module_request' === $method && empty( $module ) ) {
|
65 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-missing-module', __( 'The server did not receive a valid request. The required "module" argument is missing. Please try again.', 'better-wp-security' ) ) );
|
66 |
+
} elseif ( 'handle_module_request' === $method ) {
|
67 |
+
if ( isset( $_POST['data'] ) ) {
|
68 |
+
ITSEC_Modules::load_module_file( 'debug.php', ':active' );
|
69 |
+
/**
|
70 |
+
* Fires when an ajax request is being made to a module.
|
71 |
+
*
|
72 |
+
* At some point this will probably be replaced by a more thought-out framework, but this hook will probably power it.
|
73 |
+
*
|
74 |
+
* The dynamic portion of this hook, {$module}, refers to the module name. For example, 'notification-center'.
|
75 |
+
*
|
76 |
+
* @param array $data
|
77 |
+
*/
|
78 |
+
do_action( "itsec_debug_module_request_{$module}", $_POST['data'] );
|
79 |
+
} else {
|
80 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-module-request-missing-data', __( 'The server did not receive a valid request. The required "data" argument for the module is missing. Please try again.', 'better-wp-security' ) ) );
|
81 |
+
}
|
82 |
+
} elseif ( 'reset_scheduler' === $method ) {
|
83 |
+
ITSEC_Core::get_scheduler()->uninstall();
|
84 |
+
ITSEC_Core::get_scheduler()->register_events();
|
85 |
+
ITSEC_Response::set_response( $this->get_events_table() );
|
86 |
+
ITSEC_Response::set_success( true );
|
87 |
+
ITSEC_Response::add_message( __( 'Scheduler reset.', 'better-wp-security' ) );
|
88 |
+
} elseif ( 'run_event' === $method ) {
|
89 |
+
if ( empty( $_POST['data']['id'] ) ) {
|
90 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-run-event-missing-id', __( 'The server did not receive a valid request. The required "data.id" argument for the "run_event" method is missing.', 'better-wp-security' ) ) );
|
91 |
+
} elseif ( ! empty( $_POST['data']['data'] ) ) {
|
92 |
+
$data = json_decode( wp_unslash( $_POST['data']['data'] ), true );
|
93 |
+
|
94 |
+
if ( ! is_array( $data ) ) {
|
95 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-run-event-invalid-data', __( 'The server did not receive a valid request. The "data.data" argument for the "run_event" method is invalid JSON.', 'better-wp-security' ) ) );
|
96 |
+
} else {
|
97 |
+
ITSEC_Core::get_scheduler()->run_single_event( $_POST['data']['id'], $data );
|
98 |
+
ITSEC_Response::set_response( $this->get_events_table() );
|
99 |
+
ITSEC_Response::set_success( true );
|
100 |
+
ITSEC_Response::add_message( __( 'Event successfully run.', 'better-wp-security' ) );
|
101 |
+
}
|
102 |
+
} else {
|
103 |
+
ITSEC_Core::get_scheduler()->run_recurring_event( $_POST['data']['id'] );
|
104 |
+
ITSEC_Response::set_response( $this->get_events_table() );
|
105 |
+
ITSEC_Response::set_success( true );
|
106 |
+
ITSEC_Response::add_message( __( 'Event successfully run.', 'better-wp-security' ) );
|
107 |
+
}
|
108 |
+
} elseif ( 'load_settings' === $method ) {
|
109 |
+
ITSEC_Response::set_response( ITSEC_Modules::get_settings( $module ) );
|
110 |
+
} elseif ( 'save_settings' === $method ) {
|
111 |
+
$data = json_decode( wp_unslash( $_POST['data'] ), true );
|
112 |
+
|
113 |
+
if ( ! is_array( $data ) ) {
|
114 |
+
ITSEC_Response::set_success( false );
|
115 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-run-event-invalid-data', __( 'The server did not receive a valid request. The "data" argument for the "save_settings" method is invalid.', 'better-wp-security' ) ) );
|
116 |
+
} else {
|
117 |
+
$result = ITSEC_Modules::set_settings( $module, $data );
|
118 |
+
|
119 |
+
if ( is_wp_error( $result ) ) {
|
120 |
+
ITSEC_Response::set_success( false );
|
121 |
+
ITSEC_Response::add_error( $result );
|
122 |
+
} else {
|
123 |
+
ITSEC_Response::set_response( ITSEC_Modules::get_settings( $module ) );
|
124 |
+
|
125 |
+
if ( $result['saved'] ) {
|
126 |
+
ITSEC_Response::add_message( esc_html__( 'Module settings updated.', 'better-wp-security' ) );
|
127 |
+
}
|
128 |
+
}
|
129 |
+
}
|
130 |
+
} else {
|
131 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-unknown-method', __( 'The server did not receive a valid request. An unknown "method" argument was supplied. Please try again.', 'better-wp-security' ) ) );
|
132 |
+
}
|
133 |
+
|
134 |
+
ITSEC_Response::send_json();
|
135 |
+
}
|
136 |
+
|
137 |
+
private function show_settings_page() {
|
138 |
+
|
139 |
+
$sysinfo = $this->get_sysinfo();
|
140 |
+
|
141 |
+
$out = '';
|
142 |
+
|
143 |
+
foreach ( $sysinfo as $category => $info ) {
|
144 |
+
if ( $out ) {
|
145 |
+
$out .= "\r\n";
|
146 |
+
}
|
147 |
+
|
148 |
+
$out .= "### {$category} ###\r\n";
|
149 |
+
|
150 |
+
foreach ( $info as $label => $value ) {
|
151 |
+
$out .= "{$label}: {$value}\r\n";
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
$out = rtrim( $out );
|
156 |
+
|
157 |
+
$scheduler = ITSEC_Core::get_scheduler();
|
158 |
+
|
159 |
+
$modules = array();
|
160 |
+
|
161 |
+
foreach ( ITSEC_Modules::get_available_modules() as $module ) {
|
162 |
+
if ( ITSEC_Modules::get_settings_obj( $module ) ) {
|
163 |
+
$modules[ $module ] = $module;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
sort( $modules );
|
168 |
+
?>
|
169 |
+
<div class="wrap">
|
170 |
+
<h1>
|
171 |
+
<?php _e( 'iThemes Security', 'better-wp-security' ); ?>
|
172 |
+
<a href="<?php echo esc_url( ITSEC_Core::get_settings_page_url() ); ?>" class="page-title-action"><?php _e( 'Manage Settings', 'better-wp-security' ); ?></a>
|
173 |
+
<a href="<?php echo esc_url( apply_filters( 'itsec_support_url', 'https://wordpress.org/support/plugin/better-wp-security' ) ); ?>" class="page-title-action">
|
174 |
+
<?php _e( 'Support', 'better-wp-security' ); ?>
|
175 |
+
</a>
|
176 |
+
</h1>
|
177 |
+
|
178 |
+
<div id="itsec-messages"></div>
|
179 |
+
|
180 |
+
<div>
|
181 |
+
<h2><?php esc_html_e( 'System Info', 'better-wp-security' ); ?></h2>
|
182 |
+
<label for="itsec-system-info"><?php esc_html__( 'System Info Summary', 'better-wp-security' ); ?></label>
|
183 |
+
<textarea readonly id="itsec-system-info"><?php echo esc_textarea( $out ); ?></textarea>
|
184 |
+
</div>
|
185 |
+
|
186 |
+
<div>
|
187 |
+
<h2><?php esc_html_e( 'Settings', 'better-wp-security' ); ?></h2>
|
188 |
+
<p>
|
189 |
+
<label for="itsec-settings-module" class="screen-reader-text"><?php esc_html_e( 'Module', 'better-wp-security' ); ?></label>
|
190 |
+
<select id="itsec-settings-module">
|
191 |
+
<?php foreach ( $modules as $module ) : ?>
|
192 |
+
<option value="<?php echo esc_attr( $module ); ?>"><?php echo esc_html( $module ); ?></option>
|
193 |
+
<?php endforeach; ?>
|
194 |
+
</select>
|
195 |
+
<button class="button" id="itsec-settings-load"><?php esc_html_e( 'Load', 'better-wp-security' ); ?></button>
|
196 |
+
<button class="button" id="itsec-settings-save" disabled><?php esc_html_e( 'Save', 'better-wp-security' ) ?></button>
|
197 |
+
</p>
|
198 |
+
<label for="itsec-settings-editor" class="screen-reader-text"><?php esc_html_e( 'Edit Settings', 'better-wp-security' ); ?></label>
|
199 |
+
<textarea id="itsec-settings-editor"></textarea>
|
200 |
+
</div>
|
201 |
+
|
202 |
+
<div id="itsec-scheduler-events">
|
203 |
+
<h2><?php esc_html_e( 'Scheduler', 'better-wp-security' ); ?></h2>
|
204 |
+
<?php echo $this->get_events_table(); ?>
|
205 |
+
<p style="text-align: right;">
|
206 |
+
<code><?php echo get_class( $scheduler ); ?></code>
|
207 |
+
<button class="button" id="itsec-scheduler-reset"><?php esc_html_e( 'Reset', 'better-wp-security' ) ?></button>
|
208 |
+
</p>
|
209 |
+
</div>
|
210 |
+
|
211 |
+
<?php do_action( 'itsec_debug_page' ); ?>
|
212 |
+
</div>
|
213 |
+
<?php
|
214 |
+
}
|
215 |
+
|
216 |
+
private function get_events_table() {
|
217 |
+
$scheduler = ITSEC_Core::get_scheduler();
|
218 |
+
ob_start();
|
219 |
+
|
220 |
+
?>
|
221 |
+
|
222 |
+
<table class="widefat striped">
|
223 |
+
<thead>
|
224 |
+
<tr>
|
225 |
+
<th><?php esc_html_e( 'ID', 'better-wp-security' ) ?></th>
|
226 |
+
<th><?php esc_html_e( 'Fire At', 'better-wp-security' ) ?></th>
|
227 |
+
<th><?php esc_html_e( 'Schedule', 'better-wp-security' ) ?></th>
|
228 |
+
<th><?php esc_html_e( 'Data', 'better-wp-security' ) ?></th>
|
229 |
+
<th></th>
|
230 |
+
</tr>
|
231 |
+
</thead>
|
232 |
+
<tbody>
|
233 |
+
<?php foreach ( array_merge( $scheduler->get_recurring_events(), $scheduler->get_single_events() ) as $event ) : ?>
|
234 |
+
<tr>
|
235 |
+
<td><?php echo esc_html( $event['id'] ); ?></td>
|
236 |
+
<td><?php echo date( 'Y-m-d H:i:s', $event['fire_at'] ); ?></td>
|
237 |
+
<td><?php echo isset( $event['schedule'] ) ? $event['schedule'] : '–'; ?></td>
|
238 |
+
<td><?php $event['data'] ? ITSEC_Lib::print_r( $event['data'] ) : print( '–' ); ?></td>
|
239 |
+
<td>
|
240 |
+
<button class="button" data-id="<?php echo esc_attr( $event['id'] ); ?>"
|
241 |
+
data-data="<?php echo isset( $event['schedule'] ) ? '' : esc_attr( wp_json_encode( $event['data'] ) ); ?>">
|
242 |
+
<?php esc_html_e( 'Run', 'better-wp-security' ) ?>
|
243 |
+
</button>
|
244 |
+
</td>
|
245 |
+
</tr>
|
246 |
+
<?php endforeach; ?>
|
247 |
+
</tbody>
|
248 |
+
</table>
|
249 |
+
|
250 |
+
<?php
|
251 |
+
|
252 |
+
return ob_get_clean();
|
253 |
+
}
|
254 |
+
|
255 |
+
private function get_sysinfo() {
|
256 |
+
|
257 |
+
/** @var $wpdb wpdb */
|
258 |
+
global $wpdb;
|
259 |
+
|
260 |
+
$info = array();
|
261 |
+
|
262 |
+
$info['Site Info'] = array(
|
263 |
+
'Site URL' => site_url(),
|
264 |
+
'Home URL' => home_url(),
|
265 |
+
'Multisite' => is_multisite() ? 'Yes' : 'No'
|
266 |
+
);
|
267 |
+
|
268 |
+
$wp_config = array(
|
269 |
+
'Version' => get_bloginfo( 'version' ),
|
270 |
+
'Language' => defined( 'WPLANG' ) && WPLANG ? WPLANG : 'en_US',
|
271 |
+
'Permalink' => get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default',
|
272 |
+
'Theme' => wp_get_theme()->Name . ' ' . wp_get_theme()->Version,
|
273 |
+
'Show on Front' => get_option( 'show_on_front' )
|
274 |
+
);
|
275 |
+
|
276 |
+
if ( get_option( 'show_on_front' ) === 'page' ) {
|
277 |
+
$front_page_id = get_option( 'page_on_front' );
|
278 |
+
$blog_page_id = get_option( 'page_for_posts' );
|
279 |
+
|
280 |
+
$wp_config['Page On Front'] = $front_page_id ? get_the_title( $front_page_id ) . " (#$front_page_id)" : 'Unset';
|
281 |
+
$wp_config['Page For Posts'] = $blog_page_id ? get_the_title( $blog_page_id ) . " (#$blog_page_id)" : 'Unset';
|
282 |
+
}
|
283 |
+
|
284 |
+
$wp_config['ABSPATH'] = ABSPATH;
|
285 |
+
$wp_config['Table Prefix'] = 'Length: ' . strlen( $wpdb->prefix ) . ' Status: ' . ( strlen( $wpdb->prefix ) > 16 ? 'Too long' : 'Acceptable' );
|
286 |
+
$wp_config['WP_DEBUG'] = defined( 'WP_DEBUG' ) ? WP_DEBUG ? 'Enabled' : 'Disabled' : 'Not set';
|
287 |
+
$wp_config['WP_DEBUG_LOG'] = defined( 'WP_DEBUG_LOG' ) ? WP_DEBUG_LOG ? 'Enabled' : 'Disabled' : 'Not set';
|
288 |
+
$wp_config['SCRIPT_DEBUG'] = defined( 'SCRIPT_DEBUG' ) ? SCRIPT_DEBUG ? 'Enabled' : 'Disabled' : 'Not set';
|
289 |
+
$wp_config['Object Cache'] = wp_using_ext_object_cache() ? 'Yes' : 'No';
|
290 |
+
$wp_config['Memory Limit'] = WP_MEMORY_LIMIT;
|
291 |
+
$info['WordPress Configuration'] = $wp_config;
|
292 |
+
|
293 |
+
$defines = array(
|
294 |
+
'ITSEC_USE_CRON',
|
295 |
+
'ITSEC_DISABLE_PASSWORD_REQUIREMENTS',
|
296 |
+
'ITSEC_DEVELOPMENT',
|
297 |
+
'ITSEC_DISABLE_MODULES',
|
298 |
+
'ITSEC_DISABLE_TWO_FACTOR',
|
299 |
+
'ITSEC_DISABLE_CRON_TEST',
|
300 |
+
'ITSEC_SERVER_OVERRIDE',
|
301 |
+
'ITSEC_DOING_FILE_CHECK',
|
302 |
+
'ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE',
|
303 |
+
'ITSEC_TEST_MALWARE_SCAN_SITE_URL',
|
304 |
+
'ITSEC_TEST_MALWARE_SCAN_DISABLE_SSL_VERIFY',
|
305 |
+
'ITSEC_SUCURI_KEY',
|
306 |
+
'ITSEC_NOTIFY_USE_CRON',
|
307 |
+
'ITSEC_DISABLE_SECURITY_CHECK_PRO',
|
308 |
+
'ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION',
|
309 |
+
'ITSEC_DISABLE_PASSWORD_STRENGTH',
|
310 |
+
'ITSEC_DISABLE_INACTIVE_USER_CHECK',
|
311 |
+
);
|
312 |
+
|
313 |
+
$info['iThemes Security'] = array(
|
314 |
+
'Build' => ITSEC_Core::get_plugin_build(),
|
315 |
+
'Pro' => ITSEC_Core::is_pro(),
|
316 |
+
'Modules' => wp_sprintf( '%l', ITSEC_Modules::get_active_modules() ),
|
317 |
+
'Cron' => ITSEC_Lib::use_cron(),
|
318 |
+
'Cron Status' => ITSEC_Lib::is_cron_working(),
|
319 |
+
'Scheduler' => get_class( ITSEC_Core::get_scheduler() ),
|
320 |
+
);
|
321 |
+
|
322 |
+
foreach ( $defines as $define ) {
|
323 |
+
if ( defined( $define ) ) {
|
324 |
+
$value = constant( $define );
|
325 |
+
$info['iThemes Security'][ $define ] = $value === true ? 'Enabled' : $value === false ? 'Disabled' : $value;
|
326 |
+
}
|
327 |
+
}
|
328 |
+
|
329 |
+
$plugins = get_plugins();
|
330 |
+
$active_plugins = get_option( 'active_plugins', array() );
|
331 |
+
|
332 |
+
foreach ( $plugins as $plugin_path => $plugin ) {
|
333 |
+
|
334 |
+
if ( ! in_array( $plugin_path, $active_plugins, true ) ) {
|
335 |
+
continue;
|
336 |
+
}
|
337 |
+
|
338 |
+
$info['Active Plugins'][ $plugin['Name'] ] = $plugin['Version'];
|
339 |
+
}
|
340 |
+
|
341 |
+
foreach ( get_mu_plugins() as $plugin ) {
|
342 |
+
$info['MU Plugins'][ $plugin['Name'] ] = $plugin['Version'];
|
343 |
+
}
|
344 |
+
|
345 |
+
if ( is_multisite() ) {
|
346 |
+
$plugins = wp_get_active_network_plugins();
|
347 |
+
$active_plugins = get_site_option( 'active_sitewide_plugins', array() );
|
348 |
+
|
349 |
+
foreach ( $plugins as $plugin_path ) {
|
350 |
+
|
351 |
+
$plugin_base = plugin_basename( $plugin_path );
|
352 |
+
|
353 |
+
if ( ! array_key_exists( $plugin_base, $active_plugins ) ) {
|
354 |
+
continue;
|
355 |
+
}
|
356 |
+
|
357 |
+
$plugin = get_plugin_data( $plugin_path );
|
358 |
+
|
359 |
+
$info['Network Active Plugins'][ $plugin['Name'] ] = $plugin['Version'];
|
360 |
+
}
|
361 |
+
}
|
362 |
+
|
363 |
+
$info['Webserver Configuration'] = array(
|
364 |
+
'PHP Version' => PHP_VERSION,
|
365 |
+
'MySQL Version' => $wpdb->db_version(),
|
366 |
+
'Use MySQLi' => $wpdb->use_mysqli ? 'Yes' : 'No',
|
367 |
+
'Webserver Info' => ITSEC_Lib::get_server(),
|
368 |
+
'Host' => $this->get_host(),
|
369 |
+
);
|
370 |
+
|
371 |
+
$info['PHP Configuration'] = array(
|
372 |
+
'Safe Mode' => ini_get( 'safe_mode' ) ? 'Enabled' : 'Disabled',
|
373 |
+
'Memory Limit' => ini_get( 'memory_limit' ),
|
374 |
+
'Upload Max Size' => ini_get( 'upload_max_filesize' ),
|
375 |
+
'Post Max Size' => ini_get( 'post_max_size' ),
|
376 |
+
'Upload Max Filesize' => ini_get( 'upload_max_filesize' ),
|
377 |
+
'Time Limit' => ini_get( 'max_execution_time' ),
|
378 |
+
'Max Input Vars' => ini_get( 'max_input_vars' ),
|
379 |
+
'Display Errors' => ini_get( 'display_errors' ) ? 'On (' . ini_get( 'display_errors' ) . ')' : 'N/A'
|
380 |
+
);
|
381 |
+
|
382 |
+
$info['PHP Extensions'] = array(
|
383 |
+
'cURL' => function_exists( 'curl_init' ) ? 'Supported' : 'Not Supported',
|
384 |
+
'fsockopen' => function_exists( 'fsockopen' ) ? 'Supported' : 'Not Supported',
|
385 |
+
'SOAP Client' => class_exists( 'SoapClient' ) ? 'Installed' : 'Not Installed',
|
386 |
+
'Suhosin' => extension_loaded( 'suhosin' ) ? 'Installed' : 'Not Installed'
|
387 |
+
);
|
388 |
+
|
389 |
+
return $info;
|
390 |
+
}
|
391 |
+
|
392 |
+
private function get_host() {
|
393 |
+
|
394 |
+
if ( defined( 'WPE_APIKEY' ) ) {
|
395 |
+
$host = 'WP Engine';
|
396 |
+
} elseif ( defined( 'PAGELYBIN' ) ) {
|
397 |
+
$host = 'Pagely';
|
398 |
+
} elseif ( DB_HOST === 'localhost:/tmp/mysql5.sock' ) {
|
399 |
+
$host = 'ICDSoft';
|
400 |
+
} elseif ( DB_HOST === 'mysqlv5' ) {
|
401 |
+
$host = 'NetworkSolutions';
|
402 |
+
} elseif ( strpos( DB_HOST, 'ipagemysql.com' ) !== false ) {
|
403 |
+
$host = 'iPage';
|
404 |
+
} elseif ( strpos( DB_HOST, 'ipowermysql.com' ) !== false ) {
|
405 |
+
$host = 'IPower';
|
406 |
+
} elseif ( strpos( DB_HOST, '.gridserver.com' ) !== false ) {
|
407 |
+
$host = 'MediaTemple Grid';
|
408 |
+
} elseif ( strpos( DB_HOST, '.pair.com' ) !== false ) {
|
409 |
+
$host = 'pair Networks';
|
410 |
+
} elseif ( strpos( DB_HOST, '.stabletransit.com' ) !== false ) {
|
411 |
+
$host = 'Rackspace Cloud';
|
412 |
+
} elseif ( strpos( DB_HOST, '.sysfix.eu' ) !== false ) {
|
413 |
+
$host = 'SysFix.eu Power Hosting';
|
414 |
+
} elseif ( isset( $_SERVER['SERVER_NAME'] ) && strpos( $_SERVER['SERVER_NAME'], 'Flywheel' ) !== false ) {
|
415 |
+
$host = 'Flywheel';
|
416 |
+
} else {
|
417 |
+
// Adding a general fallback for data gathering
|
418 |
+
$host = 'DBH/' . DB_HOST . ', SRV/' . ( isset( $_SERVER['SERVER_NAME'] ) ? $_SERVER['SERVER_NAME'] : '' );
|
419 |
+
}
|
420 |
+
|
421 |
+
return $host;
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
new ITSEC_Debug_Page();
|
core/admin-pages/page-logs.php
CHANGED
@@ -37,6 +37,8 @@ final class ITSEC_Logs_Page {
|
|
37 |
}
|
38 |
|
39 |
public function add_scripts() {
|
|
|
|
|
40 |
foreach ( $this->modules as $id => $module ) {
|
41 |
$module->enqueue_scripts_and_styles();
|
42 |
}
|
37 |
}
|
38 |
|
39 |
public function add_scripts() {
|
40 |
+
ITSEC_Lib::enqueue_util();
|
41 |
+
|
42 |
foreach ( $this->modules as $id => $module ) {
|
43 |
$module->enqueue_scripts_and_styles();
|
44 |
}
|
core/admin-pages/page-settings.php
CHANGED
@@ -68,6 +68,8 @@ final class ITSEC_Settings_Page {
|
|
68 |
}
|
69 |
|
70 |
public function add_scripts() {
|
|
|
|
|
71 |
foreach ( $this->modules as $id => $module ) {
|
72 |
$module->enqueue_scripts_and_styles();
|
73 |
}
|
68 |
}
|
69 |
|
70 |
public function add_scripts() {
|
71 |
+
ITSEC_Lib::enqueue_util();
|
72 |
+
|
73 |
foreach ( $this->modules as $id => $module ) {
|
74 |
$module->enqueue_scripts_and_styles();
|
75 |
}
|
core/core.php
CHANGED
@@ -24,7 +24,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
24 |
*
|
25 |
* @access private
|
26 |
*/
|
27 |
-
private $plugin_build =
|
28 |
|
29 |
/**
|
30 |
* Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
|
@@ -163,13 +163,6 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
163 |
add_action( 'admin_bar_menu', array( $this, 'modify_admin_bar' ), 99 );
|
164 |
}
|
165 |
|
166 |
-
$disabled = defined( 'ITSEC_DISABLE_PASSWORD_REQUIREMENTS') && ITSEC_DISABLE_PASSWORD_REQUIREMENTS;
|
167 |
-
|
168 |
-
if ( ! $disabled && has_action( 'itsec_validate_password' ) ) {
|
169 |
-
$pass_requirements = new ITSEC_Lib_Password_Requirements();
|
170 |
-
$pass_requirements->run();
|
171 |
-
}
|
172 |
-
|
173 |
$login_interstitial = new ITSEC_Lib_Login_Interstitial();
|
174 |
$login_interstitial->run();
|
175 |
|
@@ -328,8 +321,13 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
328 |
}
|
329 |
|
330 |
ITSEC_Modules::register_module( 'network-brute-force', "$path/modules/ipcheck", 'default-active' );
|
|
|
|
|
|
|
|
|
|
|
331 |
ITSEC_Modules::register_module( 'ssl', "$path/modules/ssl" );
|
332 |
-
ITSEC_Modules::register_module( 'strong-passwords', "$path/modules/strong-passwords", '
|
333 |
ITSEC_Modules::register_module( 'system-tweaks', "$path/modules/system-tweaks" );
|
334 |
ITSEC_Modules::register_module( 'wordpress-salts', "$path/modules/salts", 'always-active' );
|
335 |
ITSEC_Modules::register_module( 'wordpress-tweaks', "$path/modules/wordpress-tweaks", 'default-active' );
|
24 |
*
|
25 |
* @access private
|
26 |
*/
|
27 |
+
private $plugin_build = 4097;
|
28 |
|
29 |
/**
|
30 |
* Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
|
163 |
add_action( 'admin_bar_menu', array( $this, 'modify_admin_bar' ), 99 );
|
164 |
}
|
165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
166 |
$login_interstitial = new ITSEC_Lib_Login_Interstitial();
|
167 |
$login_interstitial->run();
|
168 |
|
321 |
}
|
322 |
|
323 |
ITSEC_Modules::register_module( 'network-brute-force', "$path/modules/ipcheck", 'default-active' );
|
324 |
+
|
325 |
+
if ( ! defined( 'ITSEC_DISABLE_PASSWORD_REQUIREMENTS') || ! ITSEC_DISABLE_PASSWORD_REQUIREMENTS ) {
|
326 |
+
ITSEC_Modules::register_module( 'password-requirements', "$path/modules/password-requirements/", 'always-active' );
|
327 |
+
}
|
328 |
+
|
329 |
ITSEC_Modules::register_module( 'ssl', "$path/modules/ssl" );
|
330 |
+
ITSEC_Modules::register_module( 'strong-passwords', "$path/modules/strong-passwords", 'always-active' );
|
331 |
ITSEC_Modules::register_module( 'system-tweaks', "$path/modules/system-tweaks" );
|
332 |
ITSEC_Modules::register_module( 'wordpress-salts', "$path/modules/salts", 'always-active' );
|
333 |
ITSEC_Modules::register_module( 'wordpress-tweaks', "$path/modules/wordpress-tweaks", 'default-active' );
|
core/history.txt
CHANGED
@@ -697,4 +697,25 @@
|
|
697 |
Bug Fix: Changed the rules generated by the Filter Suspicious Query Strings feature in order to avoid blocking privacy export/erasure request confirmations.
|
698 |
4.5.1 - 2018-05-25 - Chris Jean & Timothy Jacobs
|
699 |
Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
|
700 |
-
Bug Fix: Skip recovery if File Change storage is empty.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
697 |
Bug Fix: Changed the rules generated by the Filter Suspicious Query Strings feature in order to avoid blocking privacy export/erasure request confirmations.
|
698 |
4.5.1 - 2018-05-25 - Chris Jean & Timothy Jacobs
|
699 |
Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
|
700 |
+
Bug Fix: Skip recovery if File Change storage is empty.
|
701 |
+
4.5.2 - 2018-05-31 - Chris Jean & Timothy Jacobs
|
702 |
+
Enhancement: Add UI to cancel in progress File Scan.
|
703 |
+
Tweak: Don't write to the tracked files setting if the file hash has not changed.
|
704 |
+
Bug Fix: Ensure scheduling lock is cleared by the Cron Scheduler when not proceeding with running events.
|
705 |
+
Bug Fix: Away Mode would not lock out users who were already logged-in during the "away" period.
|
706 |
+
Bug Fix: Prevent File Change from getting stuck in an infinite rescheduling loop on the first step.
|
707 |
+
4.6.0 - 2018-06-07 - Chris Jean & Timothy Jacobs
|
708 |
+
Enhancement: Introduce Password Requirements module for managing and enforcing password requirements.
|
709 |
+
Enhancement: Continually evaluate password strength for users instead of only during registration.
|
710 |
+
Enhancement: Add basic admin debug page to help diagnosing and resolving issues. Particularly with the events.
|
711 |
+
Bug Fix: Password strength would not be evaluated if password was set using custom PHP or CLI commands.
|
712 |
+
Bug Fix: Only hide "Acknowledge Weak Password" checkbox if the user was not allowed to use a weak password.
|
713 |
+
4.6.1 - 2018-06-11 - Chris Jean & Timothy Jacobs
|
714 |
+
Enhancement: Add debug settings JSON editor.
|
715 |
+
Tweak: If no last password change date is recorded for the user, treat their registration date as the last change date.
|
716 |
+
Bug Fix: If a password requirement has been disabled or is no longer available, don't consider the password as needing a change.
|
717 |
+
Bug Fix: Remove distributed storage table on uninstall.
|
718 |
+
4.6.2 - 2018-06-12 - Chris Jean & Timothy Jacobs
|
719 |
+
Bug Fix: Accessing password requirement settings would not resolve properly in some instances.
|
720 |
+
4.6.3 - 2018-06-14 - Chris Jean & Timothy Jacobs
|
721 |
+
Bug Fix: Enforce the Strong Passwords requirement during Security Check.
|
core/lib.php
CHANGED
@@ -1068,8 +1068,10 @@ final class ITSEC_Lib {
|
|
1068 |
* Enqueue the itsec_util script.
|
1069 |
*
|
1070 |
* Will only be included once per page.
|
|
|
|
|
1071 |
*/
|
1072 |
-
public static function enqueue_util() {
|
1073 |
|
1074 |
static $enqueued = false;
|
1075 |
|
@@ -1097,8 +1099,8 @@ final class ITSEC_Lib {
|
|
1097 |
|
1098 |
wp_enqueue_script( 'itsec-util', plugins_url( 'admin-pages/js/util.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build(), true );
|
1099 |
wp_localize_script( 'itsec-util', 'itsec_util', array(
|
1100 |
-
'ajax_action' => 'itsec_settings_page',
|
1101 |
-
'ajax_nonce' => wp_create_nonce( 'itsec-settings-nonce' ),
|
1102 |
'translations' => $translations,
|
1103 |
) );
|
1104 |
|
@@ -1153,4 +1155,32 @@ final class ITSEC_Lib {
|
|
1153 |
|
1154 |
return $array;
|
1155 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1156 |
}
|
1068 |
* Enqueue the itsec_util script.
|
1069 |
*
|
1070 |
* Will only be included once per page.
|
1071 |
+
*
|
1072 |
+
* @param array $args
|
1073 |
*/
|
1074 |
+
public static function enqueue_util( $args = array() ) {
|
1075 |
|
1076 |
static $enqueued = false;
|
1077 |
|
1099 |
|
1100 |
wp_enqueue_script( 'itsec-util', plugins_url( 'admin-pages/js/util.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build(), true );
|
1101 |
wp_localize_script( 'itsec-util', 'itsec_util', array(
|
1102 |
+
'ajax_action' => isset( $args['action'] ) ? $args['action'] : 'itsec_settings_page',
|
1103 |
+
'ajax_nonce' => wp_create_nonce( isset( $args['nonce'] ) ? $args['nonce'] : 'itsec-settings-nonce' ),
|
1104 |
'translations' => $translations,
|
1105 |
) );
|
1106 |
|
1155 |
|
1156 |
return $array;
|
1157 |
}
|
1158 |
+
|
1159 |
+
/**
|
1160 |
+
* Insert an element after a given key.
|
1161 |
+
*
|
1162 |
+
* @param string|int $key
|
1163 |
+
* @param array $array
|
1164 |
+
* @param string|int $new_key
|
1165 |
+
* @param mixed $new_value
|
1166 |
+
*
|
1167 |
+
* @return array
|
1168 |
+
*/
|
1169 |
+
public static function array_insert_after( $key, $array, $new_key, $new_value ) {
|
1170 |
+
if ( array_key_exists( $key, $array ) ) {
|
1171 |
+
$new = array();
|
1172 |
+
foreach ( $array as $k => $value ) {
|
1173 |
+
$new[ $k ] = $value;
|
1174 |
+
if ( $k === $key ) {
|
1175 |
+
$new[ $new_key ] = $new_value;
|
1176 |
+
}
|
1177 |
+
}
|
1178 |
+
|
1179 |
+
return $new;
|
1180 |
+
}
|
1181 |
+
|
1182 |
+
$array[ $new_key ] = $new_value;
|
1183 |
+
|
1184 |
+
return $array;
|
1185 |
+
}
|
1186 |
}
|
core/lib/class-itsec-lib-canonical-roles.php
CHANGED
@@ -122,6 +122,49 @@ final class ITSEC_Lib_Canonical_Roles {
|
|
122 |
return '';
|
123 |
}
|
124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
/**
|
126 |
* Get a list of all of the capabilities that are unique to each role.
|
127 |
*
|
122 |
return '';
|
123 |
}
|
124 |
|
125 |
+
/**
|
126 |
+
* Get the canonical role from any WordPress role.
|
127 |
+
*
|
128 |
+
* @param string $role
|
129 |
+
*
|
130 |
+
* @return string
|
131 |
+
*/
|
132 |
+
public static function get_canonical_role_from_role( $role ) {
|
133 |
+
return self::get_role_from_caps( array_keys( array_filter( wp_roles()->get_role( $role )->capabilities ) ) );
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Retrieve a canonical role for a user and a role.
|
138 |
+
*
|
139 |
+
* @param string $role
|
140 |
+
* @param WP_User $user
|
141 |
+
*
|
142 |
+
* @return string
|
143 |
+
*/
|
144 |
+
public static function get_canonical_role_from_role_and_user( $role, $user ) {
|
145 |
+
$user = ITSEC_Lib::get_user( $user );
|
146 |
+
|
147 |
+
if ( empty( $role ) ) {
|
148 |
+
$role_caps = array();
|
149 |
+
} else {
|
150 |
+
$role_caps = array_keys( array_filter( wp_roles()->get_role( $role )->capabilities ) );
|
151 |
+
}
|
152 |
+
|
153 |
+
$user_caps = array();
|
154 |
+
|
155 |
+
if ( isset( $user->caps ) ) {
|
156 |
+
$wp_roles = wp_roles();
|
157 |
+
|
158 |
+
foreach ( $user->caps as $cap => $has ) {
|
159 |
+
if ( $has && ! $wp_roles->is_role( $cap ) ) {
|
160 |
+
$user_caps[] = $has;
|
161 |
+
}
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
return self::get_role_from_caps( array_merge( $role_caps, $user_caps ) );
|
166 |
+
}
|
167 |
+
|
168 |
/**
|
169 |
* Get a list of all of the capabilities that are unique to each role.
|
170 |
*
|
core/lib/class-itsec-lib-password-requirements.php
CHANGED
@@ -1,186 +1,75 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
* Tool to manage password requirements across modules.
|
4 |
-
*
|
5 |
-
* @since 3.9.0
|
6 |
-
* @license GPLv2+
|
7 |
-
*/
|
8 |
|
9 |
/**
|
10 |
* Class ITSEC_Lib_Password_Requirements
|
11 |
*/
|
12 |
class ITSEC_Lib_Password_Requirements {
|
13 |
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
/** @var string */
|
18 |
-
private $error_message = '';
|
19 |
-
|
20 |
-
public function run() {
|
21 |
-
|
22 |
-
add_action( 'user_profile_update_errors', array( $this, 'forward_profile_pass_update' ), 0, 3 );
|
23 |
-
add_action( 'validate_password_reset', array( $this, 'forward_reset_pass' ), 10, 2 );
|
24 |
-
add_action( 'profile_update', array( $this, 'set_password_last_updated' ), 10, 2 );
|
25 |
-
|
26 |
-
add_action( 'itsec_login_interstitial_init', array( $this, 'register_interstitial' ) );
|
27 |
-
}
|
28 |
|
29 |
/**
|
30 |
-
*
|
31 |
*
|
32 |
-
* @
|
33 |
-
* @param bool $update
|
34 |
-
* @param WP_User|stdClass $user
|
35 |
*/
|
36 |
-
public function
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
} elseif ( isset( $user->ID ) && $user->ID === get_current_user_id() ) {
|
45 |
-
$context = 'profile-update';
|
46 |
-
} else {
|
47 |
-
$context = 'admin-profile-update';
|
48 |
-
}
|
49 |
-
|
50 |
-
$args = array(
|
51 |
-
'error' => $errors,
|
52 |
-
'context' => $context
|
53 |
-
);
|
54 |
-
|
55 |
-
if ( isset( $user->role ) ) {
|
56 |
-
$args['role'] = $user->role;
|
57 |
}
|
58 |
|
59 |
-
self
|
60 |
}
|
61 |
|
62 |
/**
|
63 |
-
*
|
64 |
*
|
65 |
-
* @param
|
66 |
-
* @param
|
67 |
*/
|
68 |
-
public function
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
'error' => $errors,
|
79 |
-
'context' => 'reset-password',
|
80 |
) );
|
81 |
-
}
|
82 |
-
|
83 |
-
/**
|
84 |
-
* Whenever a user object is updated, set when their password was last updated.
|
85 |
-
*
|
86 |
-
* @param int $user_id
|
87 |
-
* @param object $old_user_data
|
88 |
-
*/
|
89 |
-
public function set_password_last_updated( $user_id, $old_user_data ) {
|
90 |
-
|
91 |
-
$user = get_userdata( $user_id );
|
92 |
|
93 |
-
if (
|
|
|
|
|
|
|
94 |
return;
|
95 |
}
|
96 |
|
97 |
-
|
98 |
-
|
99 |
-
}
|
100 |
-
|
101 |
-
/**
|
102 |
-
* Register the password change interstitial.
|
103 |
-
*
|
104 |
-
* @param ITSEC_Lib_Login_Interstitial $lib
|
105 |
-
*/
|
106 |
-
public function register_interstitial( $lib ) {
|
107 |
-
$lib->register( 'update-password', array( $this, 'render_interstitial' ), array(
|
108 |
-
'show_to_user' => array( __CLASS__, 'password_change_required' ),
|
109 |
-
'info_message' => array( __CLASS__, 'get_message_for_password_change_reason' ),
|
110 |
-
'submit' => array( $this, 'submit' ),
|
111 |
-
) );
|
112 |
-
}
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Render the interstitial.
|
116 |
-
*
|
117 |
-
* @param WP_User $user
|
118 |
-
*/
|
119 |
-
public function render_interstitial( $user ) {
|
120 |
-
?>
|
121 |
-
|
122 |
-
<div class="user-pass1-wrap">
|
123 |
-
<p><label for="pass1"><?php _e( 'New Password', 'better-wp-security' ); ?></label></p>
|
124 |
-
</div>
|
125 |
-
|
126 |
-
<div class="wp-pwd">
|
127 |
-
<span class="password-input-wrapper">
|
128 |
-
<input type="password" data-reveal="1"
|
129 |
-
data-pw="<?php echo esc_attr( wp_generate_password( 16 ) ); ?>" name="pass1" id="pass1"
|
130 |
-
class="input" size="20" value="" autocomplete="off" aria-describedby="pass-strength-result"/>
|
131 |
-
</span>
|
132 |
-
<div id="pass-strength-result" class="hide-if-no-js" aria-live="polite"><?php _e( 'Strength indicator', 'better-wp-security' ); ?></div>
|
133 |
-
</div>
|
134 |
-
|
135 |
-
<p class="user-pass2-wrap">
|
136 |
-
<label for="pass2"><?php _e( 'Confirm new password' ) ?></label><br/>
|
137 |
-
<input type="password" name="pass2" id="pass2" class="input" size="20" value="" autocomplete="off"/>
|
138 |
-
</p>
|
139 |
-
|
140 |
-
<p class="description indicator-hint"><?php echo wp_get_password_hint(); ?></p>
|
141 |
-
<br class="clear"/>
|
142 |
-
|
143 |
-
<p class="submit">
|
144 |
-
<input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large"
|
145 |
-
value="<?php esc_attr_e( 'Update Password', 'better-wp-security' ); ?>"/>
|
146 |
-
</p>
|
147 |
-
|
148 |
-
<?php
|
149 |
-
}
|
150 |
-
|
151 |
-
/**
|
152 |
-
* Handle the request to update the user's password.
|
153 |
-
*
|
154 |
-
* @param WP_User $user
|
155 |
-
* @param array $data POSTed data.
|
156 |
-
*
|
157 |
-
* @return WP_Error|null
|
158 |
-
*/
|
159 |
-
public function submit( $user, $data ) {
|
160 |
-
|
161 |
-
if ( empty( $data['pass1'] ) ) {
|
162 |
-
return new WP_Error(
|
163 |
-
'itsec-password-requirements-empty-password',
|
164 |
-
__( 'Please enter your new password.', 'better-wp-security' )
|
165 |
-
);
|
166 |
}
|
167 |
|
168 |
-
|
|
|
|
|
|
|
169 |
|
170 |
-
|
171 |
-
|
|
|
172 |
}
|
173 |
|
174 |
-
$
|
175 |
-
|
176 |
-
'user_pass' => $data['pass1']
|
177 |
-
) );
|
178 |
-
|
179 |
-
if ( is_wp_error( $error ) ) {
|
180 |
-
return $error;
|
181 |
}
|
182 |
|
183 |
-
|
184 |
}
|
185 |
|
186 |
/**
|
@@ -196,14 +85,24 @@ class ITSEC_Lib_Password_Requirements {
|
|
196 |
return '';
|
197 |
}
|
198 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
/**
|
200 |
* Retrieve a human readable description as to why a password change has been required for the current user.
|
201 |
*
|
202 |
* Modules MUST HTML escape their reason strings before returning them with this filter.
|
203 |
*
|
204 |
-
* @param string
|
|
|
205 |
*/
|
206 |
-
$message = apply_filters( "itsec_password_change_requirement_description_for_{$reason}",
|
207 |
|
208 |
if ( $message ) {
|
209 |
return $message;
|
@@ -225,12 +124,12 @@ class ITSEC_Lib_Password_Requirements {
|
|
225 |
|
226 |
$args = wp_parse_args( $args, array(
|
227 |
'error' => new WP_Error(),
|
228 |
-
'context' => ''
|
229 |
) );
|
230 |
|
231 |
-
|
232 |
-
|
233 |
-
$user
|
234 |
|
235 |
if ( ! $user ) {
|
236 |
$error->add( 'invalid_user', esc_html__( 'Invalid User', 'better-wp-security' ) );
|
@@ -238,6 +137,27 @@ class ITSEC_Lib_Password_Requirements {
|
|
238 |
return $error;
|
239 |
}
|
240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
/**
|
242 |
* Fires when modules should validate a password according to their rules.
|
243 |
*
|
@@ -287,9 +207,28 @@ class ITSEC_Lib_Password_Requirements {
|
|
287 |
return false;
|
288 |
}
|
289 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
290 |
return $reason;
|
291 |
}
|
292 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
293 |
/**
|
294 |
* Get the GMT time the user's password has last been changed.
|
295 |
*
|
@@ -312,6 +251,64 @@ class ITSEC_Lib_Password_Requirements {
|
|
312 |
return $deprecated;
|
313 |
}
|
314 |
|
|
|
|
|
|
|
|
|
315 |
return $changed;
|
316 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
}
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
/**
|
4 |
* Class ITSEC_Lib_Password_Requirements
|
5 |
*/
|
6 |
class ITSEC_Lib_Password_Requirements {
|
7 |
|
8 |
+
/** @var array[] */
|
9 |
+
private static $requirements;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
/**
|
12 |
+
* Get all registered password requirements.
|
13 |
*
|
14 |
+
* @return array
|
|
|
|
|
15 |
*/
|
16 |
+
public static function get_registered() {
|
17 |
+
if ( null === self::$requirements ) {
|
18 |
+
self::$requirements = array();
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Fires when password requirements should be registered.
|
22 |
+
*/
|
23 |
+
do_action( 'itsec_register_password_requirements' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
}
|
25 |
|
26 |
+
return self::$requirements;
|
27 |
}
|
28 |
|
29 |
/**
|
30 |
+
* Register a password requirement.
|
31 |
*
|
32 |
+
* @param string $reason_code
|
33 |
+
* @param array $opts
|
34 |
*/
|
35 |
+
public static function register( $reason_code, $opts ) {
|
36 |
+
$merged = wp_parse_args( $opts, array(
|
37 |
+
'evaluate' => null,
|
38 |
+
'validate' => null,
|
39 |
+
'flag_check' => null,
|
40 |
+
'reason' => null,
|
41 |
+
'defaults' => null,
|
42 |
+
'settings_config' => null, // Callable returning label, description, render & sanitize callbacks.
|
43 |
+
'meta' => "_itsec_password_evaluation_{$reason_code}",
|
44 |
+
'evaluate_if_not_enabled' => false,
|
|
|
|
|
45 |
) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
+
if (
|
48 |
+
( array_key_exists( 'validate', $opts ) || array_key_exists( 'evaluate', $opts ) ) &&
|
49 |
+
( ! is_callable( $merged['validate'] ) || ! is_callable( $merged['evaluate'] ) )
|
50 |
+
) {
|
51 |
return;
|
52 |
}
|
53 |
|
54 |
+
if ( array_key_exists( 'flag_check', $opts ) && ! is_callable( $merged['flag_check'] ) ) {
|
55 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
}
|
57 |
|
58 |
+
if ( array_key_exists( 'defaults', $opts ) ) {
|
59 |
+
if ( ! is_array( $merged['defaults'] ) ) {
|
60 |
+
return;
|
61 |
+
}
|
62 |
|
63 |
+
if ( ! array_key_exists( 'settings_config', $opts ) ) {
|
64 |
+
return;
|
65 |
+
}
|
66 |
}
|
67 |
|
68 |
+
if ( array_key_exists( 'settings_config', $opts ) && ! is_callable( $merged['settings_config'] ) ) {
|
69 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
71 |
|
72 |
+
self::$requirements[ $reason_code ] = $merged;
|
73 |
}
|
74 |
|
75 |
/**
|
85 |
return '';
|
86 |
}
|
87 |
|
88 |
+
$message = '';
|
89 |
+
|
90 |
+
$registered = self::get_registered();
|
91 |
+
|
92 |
+
if ( isset( $registered[ $reason ] ) ) {
|
93 |
+
$settings = self::get_requirement_settings( $reason );
|
94 |
+
$message = call_user_func( $registered[ $reason ]['reason'], get_user_meta( $user->ID, $registered[ $reason ]['meta'], true ), $settings );
|
95 |
+
}
|
96 |
+
|
97 |
/**
|
98 |
* Retrieve a human readable description as to why a password change has been required for the current user.
|
99 |
*
|
100 |
* Modules MUST HTML escape their reason strings before returning them with this filter.
|
101 |
*
|
102 |
+
* @param string $message
|
103 |
+
* @param WP_User $user
|
104 |
*/
|
105 |
+
$message = apply_filters( "itsec_password_change_requirement_description_for_{$reason}", $message, $user );
|
106 |
|
107 |
if ( $message ) {
|
108 |
return $message;
|
124 |
|
125 |
$args = wp_parse_args( $args, array(
|
126 |
'error' => new WP_Error(),
|
127 |
+
'context' => '',
|
128 |
) );
|
129 |
|
130 |
+
/** @var WP_Error $error */
|
131 |
+
$error = $args['error'];
|
132 |
+
$user = $user instanceof stdClass ? $user : ITSEC_Lib::get_user( $user );
|
133 |
|
134 |
if ( ! $user ) {
|
135 |
$error->add( 'invalid_user', esc_html__( 'Invalid User', 'better-wp-security' ) );
|
137 |
return $error;
|
138 |
}
|
139 |
|
140 |
+
if ( ! empty( $user->ID ) && wp_check_password( $new_password, get_userdata( $user->ID )->user_pass, $user->ID ) ) {
|
141 |
+
$message = wp_kses( __( '<strong>ERROR</strong>: The password you have chosen appears to have been used before. You must choose a new password.', 'better-wp-security' ), array( 'strong' => array() ) );
|
142 |
+
$error->add( 'pass', $message );
|
143 |
+
|
144 |
+
return $error;
|
145 |
+
}
|
146 |
+
|
147 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
|
148 |
+
|
149 |
+
if ( isset( $args['role'] ) && $user instanceof WP_User ) {
|
150 |
+
$canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $args['role'], $user );
|
151 |
+
} elseif ( isset( $args['role'] ) ) {
|
152 |
+
$canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $args['role'] );
|
153 |
+
} elseif ( empty( $user->ID ) || ! is_numeric( $user->ID ) ) {
|
154 |
+
$canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( get_option( 'default_role', 'subscriber' ) );
|
155 |
+
} else {
|
156 |
+
$canonical = ITSEC_Lib_Canonical_Roles::get_user_role( $user );
|
157 |
+
}
|
158 |
+
|
159 |
+
$args['canonical'] = $canonical;
|
160 |
+
|
161 |
/**
|
162 |
* Fires when modules should validate a password according to their rules.
|
163 |
*
|
207 |
return false;
|
208 |
}
|
209 |
|
210 |
+
$registered = self::get_registered();
|
211 |
+
|
212 |
+
if ( isset( $registered[ $reason ] ) ) {
|
213 |
+
return self::is_requirement_enabled( $reason ) ? $reason : false;
|
214 |
+
}
|
215 |
+
|
216 |
+
if ( ! has_filter( "itsec_password_change_requirement_description_for_{$reason}" ) ) {
|
217 |
+
return false;
|
218 |
+
}
|
219 |
+
|
220 |
return $reason;
|
221 |
}
|
222 |
|
223 |
+
/**
|
224 |
+
* Globally clear all required password changes with a particular reason code.
|
225 |
+
*
|
226 |
+
* @param string $reason
|
227 |
+
*/
|
228 |
+
public static function global_clear_required_password_change( $reason ) {
|
229 |
+
delete_metadata( 'user', 0, 'itsec_password_change_required', $reason, true );
|
230 |
+
}
|
231 |
+
|
232 |
/**
|
233 |
* Get the GMT time the user's password has last been changed.
|
234 |
*
|
251 |
return $deprecated;
|
252 |
}
|
253 |
|
254 |
+
if ( ! $changed ) {
|
255 |
+
return strtotime( $user->user_registered );
|
256 |
+
}
|
257 |
+
|
258 |
return $changed;
|
259 |
}
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Is a password requirement enabled.
|
263 |
+
*
|
264 |
+
* @param string $requirement
|
265 |
+
*
|
266 |
+
* @return bool
|
267 |
+
*/
|
268 |
+
public static function is_requirement_enabled( $requirement ) {
|
269 |
+
|
270 |
+
$requirements = self::get_registered();
|
271 |
+
|
272 |
+
if ( ! isset( $requirements[ $requirement ] ) ) {
|
273 |
+
return false;
|
274 |
+
}
|
275 |
+
|
276 |
+
// If the requirement does not have any settings, than it is always enabled.
|
277 |
+
if ( null === $requirements[ $requirement ]['settings_config'] ) {
|
278 |
+
return true;
|
279 |
+
}
|
280 |
+
|
281 |
+
$enabled = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
|
282 |
+
|
283 |
+
if ( ! empty( $enabled[ $requirement ] ) ) {
|
284 |
+
return true;
|
285 |
+
}
|
286 |
+
|
287 |
+
return false;
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Get requirement settings.
|
292 |
+
*
|
293 |
+
* @param string $requirement
|
294 |
+
*
|
295 |
+
* @return array|false
|
296 |
+
*/
|
297 |
+
public static function get_requirement_settings( $requirement ) {
|
298 |
+
|
299 |
+
$requirements = self::get_registered();
|
300 |
+
|
301 |
+
if ( ! isset( $requirements[ $requirement ] ) ) {
|
302 |
+
return false;
|
303 |
+
}
|
304 |
+
|
305 |
+
if ( null === $requirements[ $requirement ]['settings_config'] ) {
|
306 |
+
return false;
|
307 |
+
}
|
308 |
+
|
309 |
+
$all_settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
|
310 |
+
$settings = isset( $all_settings[ $requirement ] ) ? $all_settings[ $requirement ] : array();
|
311 |
+
|
312 |
+
return wp_parse_args( $settings, $requirements[ $requirement ]['defaults'] );
|
313 |
+
}
|
314 |
}
|
core/lib/class-itsec-mail.php
CHANGED
@@ -1,14 +1,19 @@
|
|
1 |
<?php
|
2 |
|
3 |
final class ITSEC_Mail {
|
|
|
4 |
private $content = '';
|
|
|
|
|
|
|
5 |
private $subject = '';
|
6 |
private $recipients = array();
|
7 |
private $attachments = array();
|
8 |
private $template_path = '';
|
9 |
|
10 |
-
public function __construct() {
|
11 |
$this->template_path = dirname( __FILE__ ) . '/mail-templates/';
|
|
|
12 |
}
|
13 |
|
14 |
public function add_header( $title, $banner_title, $use_site_logo = false ) {
|
@@ -31,7 +36,7 @@ final class ITSEC_Mail {
|
|
31 |
'title' => $title,
|
32 |
);
|
33 |
|
34 |
-
$this->
|
35 |
}
|
36 |
|
37 |
public function add_footer() {
|
@@ -74,13 +79,13 @@ final class ITSEC_Mail {
|
|
74 |
|
75 |
);
|
76 |
|
77 |
-
$this->
|
78 |
|
79 |
if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG ) {
|
80 |
$this->include_debug_info();
|
81 |
}
|
82 |
|
83 |
-
$this->
|
84 |
}
|
85 |
|
86 |
public function add_user_footer() {
|
@@ -96,51 +101,79 @@ final class ITSEC_Mail {
|
|
96 |
) );
|
97 |
|
98 |
$footer .= $this->get_template( 'close.html' );
|
99 |
-
$this->
|
100 |
}
|
101 |
|
102 |
public function add_text( $content ) {
|
|
|
|
|
|
|
|
|
103 |
$module = $this->get_template( 'text.html' );
|
104 |
$module = $this->replace( $module, 'content', $content );
|
105 |
|
106 |
-
|
107 |
}
|
108 |
|
109 |
public function add_divider() {
|
110 |
-
$this->
|
|
|
|
|
|
|
|
|
111 |
}
|
112 |
|
113 |
public function add_large_text( $content ) {
|
|
|
|
|
|
|
|
|
114 |
$module = $this->get_template( 'large-text.html' );
|
115 |
$module = $this->replace( $module, 'content', $content );
|
116 |
|
117 |
-
|
118 |
}
|
119 |
|
120 |
public function add_info_box( $content, $icon_type = 'info' ) {
|
|
|
|
|
|
|
|
|
121 |
$icon_url = $this->get_image_url( $icon_type === 'warning' ? 'warning_icon_yellow' : "{$icon_type}_icon" );
|
122 |
|
123 |
$module = $this->get_template( 'info-box.html' );
|
124 |
$module = $this->replace_all( $module, compact( 'content', 'icon_url' ) );
|
125 |
|
126 |
-
|
127 |
}
|
128 |
|
129 |
public function add_details_box( $content ) {
|
|
|
|
|
|
|
|
|
130 |
$module = $this->get_template( 'details-box.html' );
|
131 |
$module = $this->replace( $module, 'content', $content );
|
132 |
|
133 |
-
|
134 |
}
|
135 |
|
136 |
public function add_large_code( $content ) {
|
|
|
|
|
|
|
|
|
137 |
$module = $this->get_template( 'large-code.html' );
|
138 |
$module = $this->replace( $module, 'content', $content );
|
139 |
|
140 |
-
|
141 |
}
|
142 |
|
143 |
public function add_section_heading( $content, $icon_type = false ) {
|
|
|
|
|
|
|
|
|
144 |
if ( empty( $icon_type ) ) {
|
145 |
$heading = $this->get_template( 'section-heading.html' );
|
146 |
$heading = $this->replace_all( $heading, compact( 'content' ) );
|
@@ -151,7 +184,7 @@ final class ITSEC_Mail {
|
|
151 |
$heading = $this->replace_all( $heading, compact( 'content', 'icon_url' ) );
|
152 |
}
|
153 |
|
154 |
-
|
155 |
}
|
156 |
|
157 |
public function add_lockouts_summary( $user_count, $host_count ) {
|
@@ -166,7 +199,7 @@ final class ITSEC_Mail {
|
|
166 |
|
167 |
$lockouts = $this->replace_all( $lockouts, $replacements );
|
168 |
|
169 |
-
$this->
|
170 |
}
|
171 |
|
172 |
public function add_file_change_summary( $added, $removed, $modified ) {
|
@@ -183,19 +216,24 @@ final class ITSEC_Mail {
|
|
183 |
|
184 |
$lockouts = $this->replace_all( $lockouts, $replacements );
|
185 |
|
186 |
-
$this->
|
187 |
}
|
188 |
|
189 |
public function add_button( $link_text, $href ) {
|
|
|
|
|
|
|
|
|
|
|
190 |
$module = $this->get_template( 'module-button.html' );
|
191 |
$module = $this->replace( $module, 'href', $href );
|
192 |
$module = $this->replace( $module, 'link_text', $link_text );
|
193 |
|
194 |
-
|
195 |
}
|
196 |
|
197 |
public function add_lockouts_table( $lockouts ) {
|
198 |
-
$entry
|
199 |
$entries = '';
|
200 |
|
201 |
foreach ( $lockouts as $lockout ) {
|
@@ -213,15 +251,15 @@ final class ITSEC_Mail {
|
|
213 |
$table = $this->get_template( 'lockouts-table.html' );
|
214 |
|
215 |
$replacements = array(
|
216 |
-
'heading_types'
|
217 |
-
'heading_until'
|
218 |
-
'heading_reason'
|
219 |
-
'entries'
|
220 |
);
|
221 |
|
222 |
$table = $this->replace_all( $table, $replacements );
|
223 |
|
224 |
-
$this->
|
225 |
}
|
226 |
|
227 |
/**
|
@@ -231,6 +269,10 @@ final class ITSEC_Mail {
|
|
231 |
* @param array[] $entries
|
232 |
*/
|
233 |
public function add_table( $headers, $entries ) {
|
|
|
|
|
|
|
|
|
234 |
|
235 |
$template = $this->get_template( 'table.html' );
|
236 |
$html = $this->build_table_header( $headers );
|
@@ -239,7 +281,7 @@ final class ITSEC_Mail {
|
|
239 |
$html .= $this->build_table_row( $entry, count( $headers ) );
|
240 |
}
|
241 |
|
242 |
-
|
243 |
}
|
244 |
|
245 |
/**
|
@@ -306,6 +348,10 @@ final class ITSEC_Mail {
|
|
306 |
* @param bool $bold_first Whether to emphasize the first item of the list.
|
307 |
*/
|
308 |
public function add_list( $items, $bold_first = false ) {
|
|
|
|
|
|
|
|
|
309 |
|
310 |
$template = $this->get_template( 'list.html' );
|
311 |
$html = '';
|
@@ -314,7 +360,7 @@ final class ITSEC_Mail {
|
|
314 |
$html .= $this->build_list_item( $item, $bold_first && 0 === $i );
|
315 |
}
|
316 |
|
317 |
-
|
318 |
}
|
319 |
|
320 |
private function build_list_item( $item, $bold = false ) {
|
@@ -323,6 +369,31 @@ final class ITSEC_Mail {
|
|
323 |
return "<li style=\"margin: 0; padding: 5px 10px;{$bold_tag}\">{$item}</li>";
|
324 |
}
|
325 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
/**
|
327 |
* Include debug info in the email.
|
328 |
*
|
@@ -359,7 +430,20 @@ final class ITSEC_Mail {
|
|
359 |
}
|
360 |
|
361 |
public function get_content() {
|
362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
363 |
}
|
364 |
|
365 |
public function set_subject( $subject, $add_site_url = true ) {
|
@@ -396,7 +480,7 @@ final class ITSEC_Mail {
|
|
396 |
}
|
397 |
|
398 |
public function set_default_recipients() {
|
399 |
-
$recipients
|
400 |
$this->set_recipients( $recipients );
|
401 |
}
|
402 |
|
@@ -421,7 +505,7 @@ final class ITSEC_Mail {
|
|
421 |
$this->set_default_subject();
|
422 |
}
|
423 |
|
424 |
-
return wp_mail( $this->recipients, $this->
|
425 |
}
|
426 |
|
427 |
/**
|
1 |
<?php
|
2 |
|
3 |
final class ITSEC_Mail {
|
4 |
+
private $name;
|
5 |
private $content = '';
|
6 |
+
private $groups = array();
|
7 |
+
private $current_group;
|
8 |
+
private $deferred = '';
|
9 |
private $subject = '';
|
10 |
private $recipients = array();
|
11 |
private $attachments = array();
|
12 |
private $template_path = '';
|
13 |
|
14 |
+
public function __construct( $name = '' ) {
|
15 |
$this->template_path = dirname( __FILE__ ) . '/mail-templates/';
|
16 |
+
$this->name = $name;
|
17 |
}
|
18 |
|
19 |
public function add_header( $title, $banner_title, $use_site_logo = false ) {
|
36 |
'title' => $title,
|
37 |
);
|
38 |
|
39 |
+
$this->add_html( $this->replace_all( $header, $replacements ), 'header' );
|
40 |
}
|
41 |
|
42 |
public function add_footer() {
|
79 |
|
80 |
);
|
81 |
|
82 |
+
$this->add_html( $this->replace_all( $footer, $replacements ) );
|
83 |
|
84 |
if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG ) {
|
85 |
$this->include_debug_info();
|
86 |
}
|
87 |
|
88 |
+
$this->add_html( $this->get_template( 'close.html' ), 'footer' );
|
89 |
}
|
90 |
|
91 |
public function add_user_footer() {
|
101 |
) );
|
102 |
|
103 |
$footer .= $this->get_template( 'close.html' );
|
104 |
+
$this->add_html( $footer, 'user-footer' );
|
105 |
}
|
106 |
|
107 |
public function add_text( $content ) {
|
108 |
+
$this->add_html( $this->get_text( $content ) );
|
109 |
+
}
|
110 |
+
|
111 |
+
public function get_text( $content ) {
|
112 |
$module = $this->get_template( 'text.html' );
|
113 |
$module = $this->replace( $module, 'content', $content );
|
114 |
|
115 |
+
return $module;
|
116 |
}
|
117 |
|
118 |
public function add_divider() {
|
119 |
+
$this->add_html( $this->get_divider() );
|
120 |
+
}
|
121 |
+
|
122 |
+
public function get_divider() {
|
123 |
+
return $this->get_template( 'divider.html' );
|
124 |
}
|
125 |
|
126 |
public function add_large_text( $content ) {
|
127 |
+
$this->add_html( $this->get_large_text( $content ) );
|
128 |
+
}
|
129 |
+
|
130 |
+
public function get_large_text( $content ) {
|
131 |
$module = $this->get_template( 'large-text.html' );
|
132 |
$module = $this->replace( $module, 'content', $content );
|
133 |
|
134 |
+
return $module;
|
135 |
}
|
136 |
|
137 |
public function add_info_box( $content, $icon_type = 'info' ) {
|
138 |
+
$this->add_html( $this->get_info_box( $content, $icon_type ) );
|
139 |
+
}
|
140 |
+
|
141 |
+
public function get_info_box( $content, $icon_type = 'info' ) {
|
142 |
$icon_url = $this->get_image_url( $icon_type === 'warning' ? 'warning_icon_yellow' : "{$icon_type}_icon" );
|
143 |
|
144 |
$module = $this->get_template( 'info-box.html' );
|
145 |
$module = $this->replace_all( $module, compact( 'content', 'icon_url' ) );
|
146 |
|
147 |
+
return $module;
|
148 |
}
|
149 |
|
150 |
public function add_details_box( $content ) {
|
151 |
+
$this->add_html( $this->get_details_box( $content ) );
|
152 |
+
}
|
153 |
+
|
154 |
+
public function get_details_box( $content ) {
|
155 |
$module = $this->get_template( 'details-box.html' );
|
156 |
$module = $this->replace( $module, 'content', $content );
|
157 |
|
158 |
+
return $module;
|
159 |
}
|
160 |
|
161 |
public function add_large_code( $content ) {
|
162 |
+
$this->add_html( $this->get_large_code( $content ) );
|
163 |
+
}
|
164 |
+
|
165 |
+
public function get_large_code( $content ) {
|
166 |
$module = $this->get_template( 'large-code.html' );
|
167 |
$module = $this->replace( $module, 'content', $content );
|
168 |
|
169 |
+
return $module;
|
170 |
}
|
171 |
|
172 |
public function add_section_heading( $content, $icon_type = false ) {
|
173 |
+
$this->add_html( $this->get_section_heading( $content, $icon_type ) );
|
174 |
+
}
|
175 |
+
|
176 |
+
public function get_section_heading( $content, $icon_type = false ) {
|
177 |
if ( empty( $icon_type ) ) {
|
178 |
$heading = $this->get_template( 'section-heading.html' );
|
179 |
$heading = $this->replace_all( $heading, compact( 'content' ) );
|
184 |
$heading = $this->replace_all( $heading, compact( 'content', 'icon_url' ) );
|
185 |
}
|
186 |
|
187 |
+
return $heading;
|
188 |
}
|
189 |
|
190 |
public function add_lockouts_summary( $user_count, $host_count ) {
|
199 |
|
200 |
$lockouts = $this->replace_all( $lockouts, $replacements );
|
201 |
|
202 |
+
$this->add_html( $lockouts, 'lockouts-summary' );
|
203 |
}
|
204 |
|
205 |
public function add_file_change_summary( $added, $removed, $modified ) {
|
216 |
|
217 |
$lockouts = $this->replace_all( $lockouts, $replacements );
|
218 |
|
219 |
+
$this->add_html( $lockouts, 'file-change-summary' );
|
220 |
}
|
221 |
|
222 |
public function add_button( $link_text, $href ) {
|
223 |
+
$this->add_html( $this->get_button( $link_text, $href ) );
|
224 |
+
}
|
225 |
+
|
226 |
+
public function get_button( $link_text, $href ) {
|
227 |
+
|
228 |
$module = $this->get_template( 'module-button.html' );
|
229 |
$module = $this->replace( $module, 'href', $href );
|
230 |
$module = $this->replace( $module, 'link_text', $link_text );
|
231 |
|
232 |
+
return $module;
|
233 |
}
|
234 |
|
235 |
public function add_lockouts_table( $lockouts ) {
|
236 |
+
$entry = $this->get_template( 'lockouts-entry.html' );
|
237 |
$entries = '';
|
238 |
|
239 |
foreach ( $lockouts as $lockout ) {
|
251 |
$table = $this->get_template( 'lockouts-table.html' );
|
252 |
|
253 |
$replacements = array(
|
254 |
+
'heading_types' => __( 'Host/User', 'better-wp-security' ),
|
255 |
+
'heading_until' => __( 'Lockout in Effect Until', 'better-wp-security' ),
|
256 |
+
'heading_reason' => __( 'Reason', 'better-wp-security' ),
|
257 |
+
'entries' => $entries,
|
258 |
);
|
259 |
|
260 |
$table = $this->replace_all( $table, $replacements );
|
261 |
|
262 |
+
$this->add_html( $table, 'lockouts-table' );
|
263 |
}
|
264 |
|
265 |
/**
|
269 |
* @param array[] $entries
|
270 |
*/
|
271 |
public function add_table( $headers, $entries ) {
|
272 |
+
$this->add_html( $this->get_table( $headers, $entries ) );
|
273 |
+
}
|
274 |
+
|
275 |
+
public function get_table( $headers, $entries ) {
|
276 |
|
277 |
$template = $this->get_template( 'table.html' );
|
278 |
$html = $this->build_table_header( $headers );
|
281 |
$html .= $this->build_table_row( $entry, count( $headers ) );
|
282 |
}
|
283 |
|
284 |
+
return $this->replace( $template, 'html', $html );
|
285 |
}
|
286 |
|
287 |
/**
|
348 |
* @param bool $bold_first Whether to emphasize the first item of the list.
|
349 |
*/
|
350 |
public function add_list( $items, $bold_first = false ) {
|
351 |
+
$this->add_html( $this->get_list( $items, $bold_first ) );
|
352 |
+
}
|
353 |
+
|
354 |
+
public function get_list( $items, $bold_first = false ) {
|
355 |
|
356 |
$template = $this->get_template( 'list.html' );
|
357 |
$html = '';
|
360 |
$html .= $this->build_list_item( $item, $bold_first && 0 === $i );
|
361 |
}
|
362 |
|
363 |
+
return $this->replace( $template, 'html', $html );
|
364 |
}
|
365 |
|
366 |
private function build_list_item( $item, $bold = false ) {
|
369 |
return "<li style=\"margin: 0; padding: 5px 10px;{$bold_tag}\">{$item}</li>";
|
370 |
}
|
371 |
|
372 |
+
private function add_html( $html, $identifier = null ) {
|
373 |
+
|
374 |
+
if ( null !== $this->current_group ) {
|
375 |
+
$this->deferred .= $html;
|
376 |
+
} elseif ( null !== $identifier ) {
|
377 |
+
$this->groups[ $identifier ] = $html;
|
378 |
+
} else {
|
379 |
+
$this->groups[] = $html;
|
380 |
+
}
|
381 |
+
}
|
382 |
+
|
383 |
+
public function start_group( $identifier ) {
|
384 |
+
$this->current_group = $identifier;
|
385 |
+
}
|
386 |
+
|
387 |
+
public function end_group() {
|
388 |
+
$group = $this->current_group;
|
389 |
+
$deferred = $this->deferred;
|
390 |
+
|
391 |
+
$this->current_group = null;
|
392 |
+
$this->deferred = '';
|
393 |
+
|
394 |
+
$this->add_html( $deferred, $group );
|
395 |
+
}
|
396 |
+
|
397 |
/**
|
398 |
* Include debug info in the email.
|
399 |
*
|
430 |
}
|
431 |
|
432 |
public function get_content() {
|
433 |
+
|
434 |
+
$groups = $this->groups;
|
435 |
+
|
436 |
+
if ( $this->name ) {
|
437 |
+
/**
|
438 |
+
* Filter the HTML groups before building the content.
|
439 |
+
*
|
440 |
+
* @param array $groups
|
441 |
+
* @param ITSEC_Mail $this
|
442 |
+
*/
|
443 |
+
$groups = apply_filters( "itsec_mail_{$this->name}", $groups, $this );
|
444 |
+
}
|
445 |
+
|
446 |
+
return implode( '', $groups );
|
447 |
}
|
448 |
|
449 |
public function set_subject( $subject, $add_site_url = true ) {
|
480 |
}
|
481 |
|
482 |
public function set_default_recipients() {
|
483 |
+
$recipients = ITSEC_Modules::get_setting( 'global', 'notification_email' );
|
484 |
$this->set_recipients( $recipients );
|
485 |
}
|
486 |
|
505 |
$this->set_default_subject();
|
506 |
}
|
507 |
|
508 |
+
return wp_mail( $this->recipients, $this->get_subject(), $this->content ? $this->content : $this->get_content(), array( 'Content-Type: text/html; charset=UTF-8' ), $this->attachments );
|
509 |
}
|
510 |
|
511 |
/**
|
core/lib/class-itsec-scheduler-cron.php
CHANGED
@@ -88,6 +88,8 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
|
|
88 |
}
|
89 |
|
90 |
if ( ! $crons = _get_cron_array() ) {
|
|
|
|
|
91 |
return;
|
92 |
}
|
93 |
|
@@ -97,12 +99,14 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
|
|
97 |
}
|
98 |
|
99 |
if ( get_transient( 'doing_cron' ) ) {
|
|
|
100 |
is_multisite() && restore_current_blog();
|
101 |
|
102 |
return;
|
103 |
}
|
104 |
|
105 |
if ( ITSEC_Lib::get_uncached_option( '_transient_doing_cron' ) ) {
|
|
|
106 |
is_multisite() && restore_current_blog();
|
107 |
|
108 |
return;
|
88 |
}
|
89 |
|
90 |
if ( ! $crons = _get_cron_array() ) {
|
91 |
+
ITSEC_Lib::release_lock( 'scheduler' );
|
92 |
+
|
93 |
return;
|
94 |
}
|
95 |
|
99 |
}
|
100 |
|
101 |
if ( get_transient( 'doing_cron' ) ) {
|
102 |
+
ITSEC_Lib::release_lock( 'scheduler' );
|
103 |
is_multisite() && restore_current_blog();
|
104 |
|
105 |
return;
|
106 |
}
|
107 |
|
108 |
if ( ITSEC_Lib::get_uncached_option( '_transient_doing_cron' ) ) {
|
109 |
+
ITSEC_Lib::release_lock( 'scheduler' );
|
110 |
is_multisite() && restore_current_blog();
|
111 |
|
112 |
return;
|
core/lib/form.php
CHANGED
@@ -462,6 +462,24 @@ final class ITSEC_Form {
|
|
462 |
$this->add_custom_input( $var, $options );
|
463 |
}
|
464 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
465 |
private function add_custom_input( $var, $options ) {
|
466 |
if ( empty( $options['type'] ) ) {
|
467 |
trigger_error( 'add_custom_input called without a type option set' );
|
462 |
$this->add_custom_input( $var, $options );
|
463 |
}
|
464 |
|
465 |
+
public function add_canonical_roles( $var, $options = array() ) {
|
466 |
+
$roles = array(
|
467 |
+
'administrator' => translate_user_role( 'Administrator' ),
|
468 |
+
'editor' => translate_user_role( 'Editor' ),
|
469 |
+
'author' => translate_user_role( 'Author' ),
|
470 |
+
'contributor' => translate_user_role( 'Contributor' ),
|
471 |
+
'subscriber' => translate_user_role( 'Subscriber' ),
|
472 |
+
);
|
473 |
+
|
474 |
+
if ( isset( $options['value'] ) ) {
|
475 |
+
$options['value'] = wp_parse_args( $options['value'], $roles );
|
476 |
+
} else {
|
477 |
+
$options['value'] = $roles;
|
478 |
+
}
|
479 |
+
|
480 |
+
$this->add_select( $var, $options );
|
481 |
+
}
|
482 |
+
|
483 |
private function add_custom_input( $var, $options ) {
|
484 |
if ( empty( $options['type'] ) ) {
|
485 |
trigger_error( 'add_custom_input called without a type option set' );
|
core/lib/mail-templates/header.html
CHANGED
@@ -77,6 +77,7 @@
|
|
77 |
#security-guide a{font-weight:bold;}
|
78 |
#footer-source-details .container-cell{line-height:200%;padding-top:60px;padding-bottom:0;}
|
79 |
#footer-source-details a{font-size:11px;font-weight:bold;line-height:200%;}
|
|
|
80 |
|
81 |
@media only screen and (max-width:600px){
|
82 |
body{width:100% !important;min-width:100% !important;}
|
77 |
#security-guide a{font-weight:bold;}
|
78 |
#footer-source-details .container-cell{line-height:200%;padding-top:60px;padding-bottom:0;}
|
79 |
#footer-source-details a{font-size:11px;font-weight:bold;line-height:200%;}
|
80 |
+
.template-container {max-width: 600px !important;}
|
81 |
|
82 |
@media only screen and (max-width:600px){
|
83 |
body{width:100% !important;min-width:100% !important;}
|
core/lib/schema.php
CHANGED
@@ -96,5 +96,6 @@ CREATE TABLE {$wpdb->base_prefix}itsec_distributed_storage (
|
|
96 |
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_log;" );
|
97 |
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_lockouts;" );
|
98 |
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_temp;" );
|
|
|
99 |
}
|
100 |
}
|
96 |
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_log;" );
|
97 |
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_lockouts;" );
|
98 |
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_temp;" );
|
99 |
+
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_distributed_storage;" );
|
100 |
}
|
101 |
}
|
core/lib/validator.php
CHANGED
@@ -305,6 +305,32 @@ abstract class ITSEC_Validator {
|
|
305 |
$error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $type ), 'better-wp-security' ), $name, $type );
|
306 |
$type = 'array';
|
307 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
308 |
} else if ( 'newline-separated-array' === $type ) {
|
309 |
$this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
|
310 |
|
@@ -394,7 +420,7 @@ abstract class ITSEC_Validator {
|
|
394 |
}
|
395 |
|
396 |
if ( false !== $error ) {
|
397 |
-
$this->add_error(
|
398 |
$this->vars_to_skip_validate_matching_types[] = $var;
|
399 |
|
400 |
if ( $prevent_save_on_error && ITSEC_Core::is_interactive() ) {
|
@@ -407,6 +433,10 @@ abstract class ITSEC_Validator {
|
|
407 |
return true;
|
408 |
}
|
409 |
|
|
|
|
|
|
|
|
|
410 |
final protected function convert_string_to_array( $string ) {
|
411 |
if ( is_string( $string ) ) {
|
412 |
$array = preg_split( "/[\r\n]+/", $string );
|
305 |
$error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $type ), 'better-wp-security' ), $name, $type );
|
306 |
$type = 'array';
|
307 |
}
|
308 |
+
} elseif ( 'canonical-roles' === $type ) {
|
309 |
+
$roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
|
310 |
+
|
311 |
+
if ( is_array( $this->settings[$var] ) ) {
|
312 |
+
$invalid_entries = array();
|
313 |
+
|
314 |
+
foreach ( $this->settings[$var] as $index => $entry ) {
|
315 |
+
$entry = sanitize_text_field( trim( $entry ) );
|
316 |
+
$this->settings[$var][$index] = $entry;
|
317 |
+
|
318 |
+
if ( empty( $entry ) ) {
|
319 |
+
unset( $this->settings[$var][$index] );
|
320 |
+
} else if ( ! in_array( $entry, $roles, true ) ) {
|
321 |
+
$invalid_entries[] = $entry;
|
322 |
+
}
|
323 |
+
}
|
324 |
+
|
325 |
+
$this->settings[$var] = array_unique( $this->settings[$var] );
|
326 |
+
|
327 |
+
if ( ! empty( $invalid_entries ) ) {
|
328 |
+
$error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
|
329 |
+
}
|
330 |
+
} else if ( ! in_array( $this->settings[$var], $roles, true ) ) {
|
331 |
+
$error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $roles ), 'better-wp-security' ), $name, $roles );
|
332 |
+
$type = 'array';
|
333 |
+
}
|
334 |
} else if ( 'newline-separated-array' === $type ) {
|
335 |
$this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
|
336 |
|
420 |
}
|
421 |
|
422 |
if ( false !== $error ) {
|
423 |
+
$this->add_error( $this->generate_error( $id, $var, $type, $error ) );
|
424 |
$this->vars_to_skip_validate_matching_types[] = $var;
|
425 |
|
426 |
if ( $prevent_save_on_error && ITSEC_Core::is_interactive() ) {
|
433 |
return true;
|
434 |
}
|
435 |
|
436 |
+
protected function generate_error( $id, $var, $type, $error ) {
|
437 |
+
return new WP_Error( "itsec-validator-$id-invalid-type-$var-$type", $error );
|
438 |
+
}
|
439 |
+
|
440 |
final protected function convert_string_to_array( $string ) {
|
441 |
if ( is_string( $string ) ) {
|
442 |
$array = preg_split( "/[\r\n]+/", $string );
|
core/lockout.php
CHANGED
@@ -84,9 +84,6 @@ final class ITSEC_Lockout {
|
|
84 |
//Set an error message on improper logout
|
85 |
add_action( 'login_head', array( $this, 'set_lockout_error' ) );
|
86 |
|
87 |
-
//Process clear lockout form
|
88 |
-
add_action( 'itsec_admin_init', array( $this, 'release_lockout' ) );
|
89 |
-
|
90 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
91 |
add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
|
92 |
|
84 |
//Set an error message on improper logout
|
85 |
add_action( 'login_head', array( $this, 'set_lockout_error' ) );
|
86 |
|
|
|
|
|
|
|
87 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
88 |
add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
|
89 |
|
core/modules/away-mode/class-itsec-away-mode.php
CHANGED
@@ -5,7 +5,7 @@ final class ITSEC_Away_Mode {
|
|
5 |
public function run() {
|
6 |
|
7 |
//Execute away mode functions on admin init
|
8 |
-
add_action( '
|
9 |
add_action( 'login_init', array( $this, 'run_active_check' ) );
|
10 |
|
11 |
add_filter( 'itsec_managed_files', array( $this, 'register_managed_file' ) );
|
@@ -78,6 +78,11 @@ final class ITSEC_Away_Mode {
|
|
78 |
* @return void
|
79 |
*/
|
80 |
public function run_active_check() {
|
|
|
|
|
|
|
|
|
|
|
81 |
$away_mode_details = self::is_active( true );
|
82 |
|
83 |
if ( $away_mode_details['active'] ) {
|
5 |
public function run() {
|
6 |
|
7 |
//Execute away mode functions on admin init
|
8 |
+
add_action( 'admin_init', array( $this, 'run_active_check' ) );
|
9 |
add_action( 'login_init', array( $this, 'run_active_check' ) );
|
10 |
|
11 |
add_filter( 'itsec_managed_files', array( $this, 'register_managed_file' ) );
|
78 |
* @return void
|
79 |
*/
|
80 |
public function run_active_check() {
|
81 |
+
|
82 |
+
if ( wp_doing_ajax() ) {
|
83 |
+
return;
|
84 |
+
}
|
85 |
+
|
86 |
$away_mode_details = self::is_active( true );
|
87 |
|
88 |
if ( $away_mode_details['active'] ) {
|
core/modules/file-change/class-itsec-file-change.php
CHANGED
@@ -77,7 +77,7 @@ class ITSEC_File_Change {
|
|
77 |
$hashes = ITSEC_Modules::get_setting( 'file-change', 'expected_hashes', array() );
|
78 |
$hash = @md5_file( $file );
|
79 |
|
80 |
-
if ( $hash ) {
|
81 |
$hashes[ $file ] = $hash;
|
82 |
ITSEC_Modules::set_setting( 'file-change', 'expected_hashes', $hashes );
|
83 |
}
|
77 |
$hashes = ITSEC_Modules::get_setting( 'file-change', 'expected_hashes', array() );
|
78 |
$hash = @md5_file( $file );
|
79 |
|
80 |
+
if ( $hash && ( ! isset( $hashes[ $file ] ) || $hashes[ $file ] !== $hash ) ) {
|
81 |
$hashes[ $file ] = $hash;
|
82 |
ITSEC_Modules::set_setting( 'file-change', 'expected_hashes', $hashes );
|
83 |
}
|
core/modules/file-change/css/settings.css
CHANGED
@@ -90,3 +90,17 @@ UL.jqueryFileTree LI {
|
|
90 |
width : 100%;
|
91 |
}
|
92 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
width : 100%;
|
91 |
}
|
92 |
}
|
93 |
+
|
94 |
+
#itsec-file-change-abort {
|
95 |
+
color: #a00;
|
96 |
+
text-decoration: none;
|
97 |
+
border-color: transparent;
|
98 |
+
box-shadow: none;
|
99 |
+
background: transparent;
|
100 |
+
}
|
101 |
+
|
102 |
+
#itsec-file-change-abort:hover {
|
103 |
+
background: #d54e21;
|
104 |
+
color: #fff;
|
105 |
+
border-color: #d54e21;
|
106 |
+
}
|
core/modules/file-change/js/settings-page.js
CHANGED
@@ -62,4 +62,19 @@ jQuery( document ).ready( function ( $ ) {
|
|
62 |
}
|
63 |
|
64 |
initializeScan();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
} );
|
62 |
}
|
63 |
|
64 |
initializeScan();
|
65 |
+
|
66 |
+
$( document ).on( 'click', '#itsec-file-change-abort', function () {
|
67 |
+
var $this = $( this );
|
68 |
+
|
69 |
+
$this.prop( 'disabled', true );
|
70 |
+
|
71 |
+
itsecUtil.sendModuleAJAXRequest( 'file-change', { method: 'abort' }, function ( results ) {
|
72 |
+
var $button = $( '#itsec-file-change-one_time_check' );
|
73 |
+
$button.prop( 'disabled', false );
|
74 |
+
$button.prop( 'class', 'button-primary' );
|
75 |
+
$button.val( ITSECFileChangeScannerl10n.button_text );
|
76 |
+
|
77 |
+
$this.remove();
|
78 |
+
} );
|
79 |
+
} );
|
80 |
} );
|
core/modules/file-change/logs.php
CHANGED
@@ -44,7 +44,17 @@ final class ITSEC_File_Change_Logs {
|
|
44 |
} elseif ( 'recovery-scheduled' === $code ) {
|
45 |
$entry['description'] = esc_html__( 'Recovery Scheduled', 'better-wp-security' );
|
46 |
} elseif ( 'file-scan-aborted' === $code ) {
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
} elseif ( 'rescheduling' === $code ) {
|
49 |
$entry['description'] = esc_html__( 'Rescheduling', 'better-wp-security' );
|
50 |
}
|
44 |
} elseif ( 'recovery-scheduled' === $code ) {
|
45 |
$entry['description'] = esc_html__( 'Recovery Scheduled', 'better-wp-security' );
|
46 |
} elseif ( 'file-scan-aborted' === $code ) {
|
47 |
+
if ( ! empty( $code_data[0] ) ) {
|
48 |
+
if ( $user = get_userdata( $code_data[0] ) ) {
|
49 |
+
$by = $user->display_name;
|
50 |
+
} else {
|
51 |
+
$by = "#{$code_data[0]}";
|
52 |
+
}
|
53 |
+
|
54 |
+
$entry['description'] = sprintf( esc_html__( 'Scan Cancelled by %s', 'better-wp-security' ), $by );
|
55 |
+
} else {
|
56 |
+
$entry['description'] = esc_html__( 'Scan Failed', 'better-wp-security' );
|
57 |
+
}
|
58 |
} elseif ( 'rescheduling' === $code ) {
|
59 |
$entry['description'] = esc_html__( 'Rescheduling', 'better-wp-security' );
|
60 |
}
|
core/modules/file-change/scanner.php
CHANGED
@@ -21,7 +21,6 @@ do_action( 'itsec_load_file_change_scanner' );
|
|
21 |
class ITSEC_File_Change_Scanner {
|
22 |
|
23 |
const DESTROYED = 'itsec_file_change_scan_destroyed';
|
24 |
-
const FILE_LIST = 'itsec_file_list';
|
25 |
|
26 |
const C_ADMIN = 'admin';
|
27 |
const C_INCLUDES = 'includes';
|
@@ -287,7 +286,7 @@ class ITSEC_File_Change_Scanner {
|
|
287 |
$job_data['step'] = $store['step'];
|
288 |
$job_data['chunk'] = $store['chunk'];
|
289 |
|
290 |
-
if ( 'get-files' === $job_data['step'] && self::C_ADMIN === $job_data['chunk'] ) {
|
291 |
ITSEC_Log::add_debug( 'file_change', 'recovery-failed-first-loop' );
|
292 |
|
293 |
self::abort();
|
@@ -319,8 +318,10 @@ class ITSEC_File_Change_Scanner {
|
|
319 |
|
320 |
/**
|
321 |
* Abort an in-progress scan.
|
|
|
|
|
322 |
*/
|
323 |
-
public static function abort() {
|
324 |
$storage = ITSEC_File_Change::make_progress_storage();
|
325 |
|
326 |
if ( 'file-change-fast' === $storage->get( 'id' ) ) {
|
@@ -334,11 +335,20 @@ class ITSEC_File_Change_Scanner {
|
|
334 |
ITSEC_Log::add_process_stop( $process, array( 'aborted' => true ) );
|
335 |
}
|
336 |
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
|
343 |
$storage->clear();
|
344 |
update_site_option( self::DESTROYED, ITSEC_Core::get_current_time_gmt() );
|
21 |
class ITSEC_File_Change_Scanner {
|
22 |
|
23 |
const DESTROYED = 'itsec_file_change_scan_destroyed';
|
|
|
24 |
|
25 |
const C_ADMIN = 'admin';
|
26 |
const C_INCLUDES = 'includes';
|
286 |
$job_data['step'] = $store['step'];
|
287 |
$job_data['chunk'] = $store['chunk'];
|
288 |
|
289 |
+
if ( 1 === $job_data['loop_item'] || ( 'get-files' === $job_data['step'] && self::C_ADMIN === $job_data['chunk'] ) ) {
|
290 |
ITSEC_Log::add_debug( 'file_change', 'recovery-failed-first-loop' );
|
291 |
|
292 |
self::abort();
|
318 |
|
319 |
/**
|
320 |
* Abort an in-progress scan.
|
321 |
+
*
|
322 |
+
* @param bool $user_initiated
|
323 |
*/
|
324 |
+
public static function abort( $user_initiated = false ) {
|
325 |
$storage = ITSEC_File_Change::make_progress_storage();
|
326 |
|
327 |
if ( 'file-change-fast' === $storage->get( 'id' ) ) {
|
335 |
ITSEC_Log::add_process_stop( $process, array( 'aborted' => true ) );
|
336 |
}
|
337 |
|
338 |
+
if ( $user_initiated ) {
|
339 |
+
$user = get_current_user_id();
|
340 |
+
ITSEC_Log::add_warning( 'file_change', "file-scan-aborted::{$user}", array(
|
341 |
+
'id' => $storage->get( 'id' ),
|
342 |
+
'step' => $storage->get( 'step' ),
|
343 |
+
'chunk' => $storage->get( 'chunk' ),
|
344 |
+
) );
|
345 |
+
} else {
|
346 |
+
ITSEC_Log::add_fatal_error( 'file_change', 'file-scan-aborted', array(
|
347 |
+
'id' => $storage->get( 'id' ),
|
348 |
+
'step' => $storage->get( 'step' ),
|
349 |
+
'chunk' => $storage->get( 'chunk' ),
|
350 |
+
) );
|
351 |
+
}
|
352 |
|
353 |
$storage->clear();
|
354 |
update_site_option( self::DESTROYED, ITSEC_Core::get_current_time_gmt() );
|
core/modules/file-change/settings-page.php
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
<?php
|
2 |
|
3 |
final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
|
4 |
-
private $script_version =
|
5 |
|
6 |
|
7 |
public function __construct() {
|
@@ -26,8 +26,9 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
|
|
26 |
require_once( dirname( __FILE__ ) . '/admin.php' );
|
27 |
}
|
28 |
|
|
|
29 |
ITSEC_File_Change_Admin::enqueue_scanner();
|
30 |
-
wp_enqueue_script( 'itsec-file-change-settings-script', plugins_url( 'js/settings-page.js', __FILE__ ), array( 'jquery', 'itsec-file-change-scanner' ), $this->script_version, true );
|
31 |
wp_localize_script( 'itsec-file-change-settings-script', 'itsec_file_change_settings', $vars );
|
32 |
|
33 |
|
@@ -54,6 +55,11 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
|
|
54 |
} else {
|
55 |
ITSEC_Response::set_success( true );
|
56 |
}
|
|
|
|
|
|
|
|
|
|
|
57 |
} else if ( 'get-filetree-data' === $data['method'] ) {
|
58 |
ITSEC_Response::set_response( $this->get_filetree_data( $data ) );
|
59 |
}
|
@@ -81,7 +87,7 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
|
|
81 |
|
82 |
require_once( dirname( __FILE__ ) . '/scanner.php' );
|
83 |
|
84 |
-
if ( ITSEC_File_Change_Scanner::is_running() ) {
|
85 |
$status = ITSEC_File_Change_Scanner::get_status();
|
86 |
|
87 |
$button = array(
|
@@ -98,7 +104,12 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
|
|
98 |
?>
|
99 |
<div class="hide-if-no-js">
|
100 |
<p><?php _e( "Press the button below to scan your site's files for changes. Note that if changes are found this will take you to the logs page for details.", 'better-wp-security' ); ?></p>
|
101 |
-
<p
|
|
|
|
|
|
|
|
|
|
|
102 |
<div id="itsec_file_change_status"></div>
|
103 |
</div>
|
104 |
|
1 |
<?php
|
2 |
|
3 |
final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
|
4 |
+
private $script_version = 4;
|
5 |
|
6 |
|
7 |
public function __construct() {
|
26 |
require_once( dirname( __FILE__ ) . '/admin.php' );
|
27 |
}
|
28 |
|
29 |
+
ITSEC_Lib::enqueue_util();
|
30 |
ITSEC_File_Change_Admin::enqueue_scanner();
|
31 |
+
wp_enqueue_script( 'itsec-file-change-settings-script', plugins_url( 'js/settings-page.js', __FILE__ ), array( 'jquery', 'itsec-file-change-scanner', 'itsec-util' ), $this->script_version, true );
|
32 |
wp_localize_script( 'itsec-file-change-settings-script', 'itsec_file_change_settings', $vars );
|
33 |
|
34 |
|
55 |
} else {
|
56 |
ITSEC_Response::set_success( true );
|
57 |
}
|
58 |
+
} elseif ( 'abort' === $data['method'] ) {
|
59 |
+
require_once( dirname( __FILE__ ) . '/scanner.php' );
|
60 |
+
ITSEC_File_Change_Scanner::abort( true );
|
61 |
+
|
62 |
+
ITSEC_Response::set_success( true );
|
63 |
} else if ( 'get-filetree-data' === $data['method'] ) {
|
64 |
ITSEC_Response::set_response( $this->get_filetree_data( $data ) );
|
65 |
}
|
87 |
|
88 |
require_once( dirname( __FILE__ ) . '/scanner.php' );
|
89 |
|
90 |
+
if ( $is_running = ITSEC_File_Change_Scanner::is_running() ) {
|
91 |
$status = ITSEC_File_Change_Scanner::get_status();
|
92 |
|
93 |
$button = array(
|
104 |
?>
|
105 |
<div class="hide-if-no-js">
|
106 |
<p><?php _e( "Press the button below to scan your site's files for changes. Note that if changes are found this will take you to the logs page for details.", 'better-wp-security' ); ?></p>
|
107 |
+
<p>
|
108 |
+
<?php $form->add_button( 'one_time_check', $button ); ?>
|
109 |
+
<?php if ( $is_running ) : ?>
|
110 |
+
<?php $form->add_button( 'abort', array( 'value' => _x( 'Cancel', 'Cancel File Change scan.', 'better-wp-security' ), 'class' => 'button' ) ); ?>
|
111 |
+
<?php endif; ?>
|
112 |
+
</p>
|
113 |
<div id="itsec_file_change_status"></div>
|
114 |
</div>
|
115 |
|
core/modules/notification-center/class-notification-center.php
CHANGED
@@ -411,12 +411,14 @@ final class ITSEC_Notification_Center {
|
|
411 |
/**
|
412 |
* Initialize a Mail instance.
|
413 |
*
|
|
|
|
|
414 |
* @return ITSEC_Mail
|
415 |
*/
|
416 |
-
public function mail() {
|
417 |
require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-mail.php' );
|
418 |
|
419 |
-
return new ITSEC_Mail();
|
420 |
}
|
421 |
|
422 |
/**
|
411 |
/**
|
412 |
* Initialize a Mail instance.
|
413 |
*
|
414 |
+
* @param string $name
|
415 |
+
*
|
416 |
* @return ITSEC_Mail
|
417 |
*/
|
418 |
+
public function mail( $name = '' ) {
|
419 |
require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-mail.php' );
|
420 |
|
421 |
+
return new ITSEC_Mail( $name );
|
422 |
}
|
423 |
|
424 |
/**
|
core/modules/notification-center/debug.php
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class ITSEC_Notification_Center_Debug
|
5 |
+
*/
|
6 |
+
class ITSEC_Notification_Center_Debug {
|
7 |
+
|
8 |
+
public function __construct() {
|
9 |
+
add_action( 'itsec_debug_page', array( $this, 'render' ) );
|
10 |
+
add_action( 'itsec_debug_page_enqueue', array( $this, 'enqueue_scripts_and_styles' ) );
|
11 |
+
add_action( 'itsec_debug_module_request_notification-center', array( $this, 'handle_ajax_request' ) );
|
12 |
+
}
|
13 |
+
|
14 |
+
public function enqueue_scripts_and_styles() {
|
15 |
+
wp_enqueue_script( 'itsec-notification-center-debug', plugins_url( 'js/debug.js', __FILE__ ), array( 'itsec-util' ), ITSEC_Core::get_plugin_build() );
|
16 |
+
}
|
17 |
+
|
18 |
+
public function handle_ajax_request( $data ) {
|
19 |
+
if ( empty( $data['id'] ) ) {
|
20 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-notification-center-missing-id', __( 'The server did not receive a valid request. The notification id is missing.', 'better-wp-security' ) ) );
|
21 |
+
|
22 |
+
return;
|
23 |
+
}
|
24 |
+
|
25 |
+
$result = ITSEC_Core::get_notification_center()->send_scheduled_notifications( array( $data['id'] ), ! empty( $data['silent'] ) );
|
26 |
+
|
27 |
+
if ( is_wp_error( $result ) ) {
|
28 |
+
ITSEC_Response::add_error( $result );
|
29 |
+
} elseif ( ! $result ) {
|
30 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-notification-center-send-failed', __( 'The server could not send the requested notification.', 'better-wp-security' ) ) );
|
31 |
+
} else {
|
32 |
+
ITSEC_Response::set_response( $this->get_table() );
|
33 |
+
ITSEC_Response::set_success( true );
|
34 |
+
ITSEC_Response::add_message( __( 'Notification sent.', 'better-wp-security' ) );
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Render our data to the Debug Page.
|
40 |
+
*/
|
41 |
+
public function render() {
|
42 |
+
?>
|
43 |
+
|
44 |
+
<div id="itsec-notification-center-notifications">
|
45 |
+
<h2><?php esc_html_e( 'Notification Center', 'better-wp-security' ); ?></h2>
|
46 |
+
<?php echo $this->get_table(); ?>
|
47 |
+
</div>
|
48 |
+
|
49 |
+
<?php
|
50 |
+
}
|
51 |
+
|
52 |
+
private function get_table() {
|
53 |
+
$nc = ITSEC_Core::get_notification_center();
|
54 |
+
ob_start();
|
55 |
+
?>
|
56 |
+
<table class="widefat striped">
|
57 |
+
<thead>
|
58 |
+
<tr>
|
59 |
+
<th><?php esc_html_e( 'ID', 'better-wp-security' ) ?></th>
|
60 |
+
<th><?php esc_html_e( 'Last Sent', 'better-wp-security' ) ?></th>
|
61 |
+
<th><?php esc_html_e( 'Next Send', 'better-wp-security' ) ?></th>
|
62 |
+
<th><?php esc_html_e( 'Schedule', 'better-wp-security' ) ?></th>
|
63 |
+
<th></th>
|
64 |
+
</tr>
|
65 |
+
</thead>
|
66 |
+
<tbody>
|
67 |
+
<?php foreach ( $nc->get_notifications() as $slug => $notification ) : $scheduled = ITSEC_Notification_Center::S_NONE !== $notification['schedule']; ?>
|
68 |
+
<tr>
|
69 |
+
<td><?php echo esc_html( $slug ); ?></td>
|
70 |
+
<td><?php echo $scheduled ? date( 'Y-m-d H:i:s', $nc->get_last_sent( $slug ) ) : '–'; ?></td>
|
71 |
+
<td><?php echo $scheduled ? date( 'Y-m-d H:i:s', $nc->get_next_send_time( $slug ) ) : '–'; ?></td>
|
72 |
+
<td><?php echo $nc->get_schedule( $slug ); ?></td>
|
73 |
+
<td>
|
74 |
+
<?php if ( $scheduled ): ?>
|
75 |
+
<button class="button itsec__send-notification itsec__send-notification--force" data-id="<?php echo esc_attr( $slug ); ?>">
|
76 |
+
<?php esc_html_e( 'Force', 'better-wp-security' ) ?>
|
77 |
+
</button>
|
78 |
+
<button class="button itsec__send-notification itsec__send-notification--silent" data-id="<?php echo esc_attr( $slug ); ?>">
|
79 |
+
<?php esc_html_e( 'Silent', 'better-wp-security' ) ?>
|
80 |
+
</button>
|
81 |
+
<?php endif; ?>
|
82 |
+
</td>
|
83 |
+
</tr>
|
84 |
+
<?php endforeach; ?>
|
85 |
+
</tbody>
|
86 |
+
</table>
|
87 |
+
<?php
|
88 |
+
return ob_get_clean();
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
new ITSEC_Notification_Center_Debug();
|
core/modules/notification-center/js/debug.js
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function ( $, itsecUtil ) {
|
2 |
+
"use strict";
|
3 |
+
|
4 |
+
$( function () {
|
5 |
+
$( '#itsec-notification-center-notifications' ).on( 'click', '.button', function () {
|
6 |
+
|
7 |
+
var $btn = $( this );
|
8 |
+
$btn.prop( 'disabled', true );
|
9 |
+
|
10 |
+
itsecUtil.sendModuleAJAXRequest( 'notification-center', { id: $btn.data( 'id' ), silent: $btn.hasClass( 'itsec__send-notification--silent' ) ? 1 : 0 }, function ( response ) {
|
11 |
+
|
12 |
+
$btn.prop( 'disabled', false );
|
13 |
+
|
14 |
+
if ( response.success ) {
|
15 |
+
$( 'table', '#itsec-notification-center-notifications' ).replaceWith( response.response );
|
16 |
+
}
|
17 |
+
|
18 |
+
itsecUtil.displayNotices( response, $( '#itsec-messages' ) );
|
19 |
+
} );
|
20 |
+
} );
|
21 |
+
} );
|
22 |
+
})( jQuery, window.itsecUtil );
|
core/modules/password-requirements/active.php
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once( dirname( __FILE__ ) . '/class-itsec-password-requirements.php' );
|
4 |
+
|
5 |
+
$requirements = new ITSEC_Password_Requirements();
|
6 |
+
$requirements->run();
|
core/modules/password-requirements/class-itsec-password-requirements.php
ADDED
@@ -0,0 +1,480 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class ITSEC_Password_Requirements
|
5 |
+
*/
|
6 |
+
class ITSEC_Password_Requirements {
|
7 |
+
|
8 |
+
const META_KEY = '_itsec_password_requirements';
|
9 |
+
|
10 |
+
public function run() {
|
11 |
+
|
12 |
+
add_action( 'user_profile_update_errors', array( $this, 'forward_profile_pass_update' ), 0, 3 );
|
13 |
+
add_action( 'validate_password_reset', array( $this, 'forward_reset_pass' ), 10, 2 );
|
14 |
+
|
15 |
+
add_action( 'profile_update', array( $this, 'handle_update_user' ), 10, 2 );
|
16 |
+
add_action( 'password_reset', array( $this, 'handle_password_reset' ), 10, 2 );
|
17 |
+
add_filter( 'wp_authenticate_user', array( $this, 'check_password_on_login' ), 999, 2 );
|
18 |
+
|
19 |
+
add_action( 'add_user_role', array( $this, 'handle_role_change' ) );
|
20 |
+
add_action( 'set_user_role', array( $this, 'handle_role_change' ) );
|
21 |
+
add_action( 'remove_user_role', array( $this, 'handle_role_change' ) );
|
22 |
+
|
23 |
+
add_action( 'itsec_validate_password', array( $this, 'validate_password' ), 10, 4 );
|
24 |
+
|
25 |
+
add_action( 'wp_login', array( $this, 'flag_check' ), 9, 2 );
|
26 |
+
|
27 |
+
add_action( 'itsec_login_interstitial_init', array( $this, 'register_interstitial' ) );
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* When a user's password is updated, or a new user created, verify that the new password is valid.
|
32 |
+
*
|
33 |
+
* @param WP_Error $errors
|
34 |
+
* @param bool $update
|
35 |
+
* @param WP_User|stdClass $user
|
36 |
+
*/
|
37 |
+
public function forward_profile_pass_update( $errors, $update, $user ) {
|
38 |
+
|
39 |
+
if ( $errors->get_error_message( 'pass' ) ) {
|
40 |
+
return;
|
41 |
+
}
|
42 |
+
|
43 |
+
if ( isset( $user->user_pass ) ) {
|
44 |
+
$this->handle_profile_update_password( $errors, $update, $user );
|
45 |
+
} elseif ( $update && isset( $user->role ) ) {
|
46 |
+
$this->handle_profile_update_role( $errors, $user );
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Handle the password being updated for a user.
|
52 |
+
*
|
53 |
+
* @param WP_Error $errors
|
54 |
+
* @param bool $update
|
55 |
+
* @param WP_User|stdClass $user
|
56 |
+
*/
|
57 |
+
private function handle_profile_update_password( $errors, $update, $user ) {
|
58 |
+
if ( ! $update ) {
|
59 |
+
$context = 'admin-user-create';
|
60 |
+
} elseif ( isset( $user->ID ) && $user->ID === get_current_user_id() ) {
|
61 |
+
$context = 'profile-update';
|
62 |
+
} else {
|
63 |
+
$context = 'admin-profile-update';
|
64 |
+
}
|
65 |
+
|
66 |
+
$args = array(
|
67 |
+
'error' => $errors,
|
68 |
+
'context' => $context
|
69 |
+
);
|
70 |
+
|
71 |
+
if ( isset( $user->role ) ) {
|
72 |
+
$args['role'] = $user->role;
|
73 |
+
}
|
74 |
+
|
75 |
+
ITSEC_Lib_Password_Requirements::validate_password( $user, $user->user_pass, $args );
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Handle the user's role being updated.
|
80 |
+
*
|
81 |
+
* @param WP_Error $errors
|
82 |
+
* @param WP_User|stdClass $user
|
83 |
+
*/
|
84 |
+
private function handle_profile_update_role( $errors, $user ) {
|
85 |
+
|
86 |
+
$settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
|
87 |
+
|
88 |
+
foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
|
89 |
+
|
90 |
+
if ( ! $requirement['validate'] || ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
|
91 |
+
continue;
|
92 |
+
}
|
93 |
+
|
94 |
+
$evaluation = get_user_meta( $user->ID, $requirement['meta'], true );
|
95 |
+
|
96 |
+
if ( '' === $evaluation ) {
|
97 |
+
continue;
|
98 |
+
}
|
99 |
+
|
100 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
|
101 |
+
|
102 |
+
$args = array(
|
103 |
+
'role' => $user->role,
|
104 |
+
'canonical' => ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $user->role, $user ),
|
105 |
+
);
|
106 |
+
|
107 |
+
$validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], $args );
|
108 |
+
|
109 |
+
if ( true === $validated ) {
|
110 |
+
continue;
|
111 |
+
}
|
112 |
+
|
113 |
+
$message = $validated ? $validated : esc_html__( "The provided password does not meet this site's requirements.", 'better-wp-security' );
|
114 |
+
$errors->add( 'pass', $message );
|
115 |
+
}
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* When a user attempts to reset their password, verify that the new password is valid.
|
120 |
+
*
|
121 |
+
* @param WP_Error $errors
|
122 |
+
* @param WP_User $user
|
123 |
+
*/
|
124 |
+
public function forward_reset_pass( $errors, $user ) {
|
125 |
+
|
126 |
+
if ( ! isset( $_POST['pass1'] ) || is_wp_error( $user ) ) {
|
127 |
+
// The validate_password_reset action fires when first rendering the reset page and when handling the form
|
128 |
+
// submissions. Since the pass1 data is missing, this must be the initial page render. So, we don't need to
|
129 |
+
// do anything yet.
|
130 |
+
return;
|
131 |
+
}
|
132 |
+
|
133 |
+
ITSEC_Lib_Password_Requirements::validate_password( $user, $_POST['pass1'], array(
|
134 |
+
'error' => $errors,
|
135 |
+
'context' => 'reset-password',
|
136 |
+
) );
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Whenever a user object is updated, set when their password was last updated.
|
141 |
+
*
|
142 |
+
* @param int $user_id
|
143 |
+
* @param object $old_user_data
|
144 |
+
*/
|
145 |
+
public function handle_update_user( $user_id, $old_user_data ) {
|
146 |
+
|
147 |
+
$user = get_userdata( $user_id );
|
148 |
+
|
149 |
+
if ( $user->user_pass === $old_user_data->user_pass ) {
|
150 |
+
return;
|
151 |
+
}
|
152 |
+
|
153 |
+
$this->handle_password_updated( $user );
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* When a user resets their password, update the last change time.
|
158 |
+
*
|
159 |
+
* For some unknown reason, the password reset routine uses {@see wp_set_password()} instead of {@see wp_update_user()}.
|
160 |
+
*
|
161 |
+
* @param WP_User $user
|
162 |
+
* @param string $new_password
|
163 |
+
*/
|
164 |
+
public function handle_password_reset( $user, $new_password ) {
|
165 |
+
$this->handle_password_updated( $user );
|
166 |
+
$this->handle_plain_text_password_available( $user, $new_password );
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* When a user logs in, if their password hasn't been validated yet,
|
171 |
+
* validate it.
|
172 |
+
*
|
173 |
+
* @param WP_User $user
|
174 |
+
* @param string $password
|
175 |
+
*
|
176 |
+
* @return WP_User
|
177 |
+
*/
|
178 |
+
public function check_password_on_login( $user, $password ) {
|
179 |
+
|
180 |
+
if ( ! $user instanceof WP_User ) {
|
181 |
+
return $user;
|
182 |
+
}
|
183 |
+
|
184 |
+
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
|
185 |
+
return $user;
|
186 |
+
}
|
187 |
+
|
188 |
+
$this->handle_plain_text_password_available( $user, $password );
|
189 |
+
|
190 |
+
return $user;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* When a password is updated, set the last updated time and delete any pending required change.
|
195 |
+
*
|
196 |
+
* @param WP_User $user
|
197 |
+
*/
|
198 |
+
protected function handle_password_updated( $user ) {
|
199 |
+
delete_user_meta( $user->ID, 'itsec_password_change_required' );
|
200 |
+
update_user_meta( $user->ID, 'itsec_last_password_change', ITSEC_Core::get_current_time_gmt() );
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* When a plain text password is available, we perform any evaluations that have not yet been performed for this password.
|
205 |
+
*
|
206 |
+
* @param WP_User $user
|
207 |
+
* @param string $password
|
208 |
+
*/
|
209 |
+
protected function handle_plain_text_password_available( $user, $password ) {
|
210 |
+
|
211 |
+
$config = wp_parse_args( get_user_meta( $user->ID, self::META_KEY, true ), array(
|
212 |
+
'evaluation_times' => array(),
|
213 |
+
) );
|
214 |
+
|
215 |
+
$last_updated = ITSEC_Lib_Password_Requirements::password_last_changed( $user );
|
216 |
+
|
217 |
+
$settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
|
218 |
+
|
219 |
+
foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
|
220 |
+
|
221 |
+
if ( ! $requirement['evaluate'] ) {
|
222 |
+
continue;
|
223 |
+
}
|
224 |
+
|
225 |
+
if ( ! $requirement['evaluate_if_not_enabled'] && ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
|
226 |
+
continue;
|
227 |
+
}
|
228 |
+
|
229 |
+
if ( isset( $config['evaluation_times'][ $code ] ) && $config['evaluation_times'][ $code ] >= $last_updated ) {
|
230 |
+
continue;
|
231 |
+
}
|
232 |
+
|
233 |
+
$evaluation = call_user_func( $requirement['evaluate'], $password, $user );
|
234 |
+
|
235 |
+
if ( is_wp_error( $evaluation ) ) {
|
236 |
+
continue;
|
237 |
+
}
|
238 |
+
|
239 |
+
$config['evaluation_times'][ $code ] = ITSEC_Core::get_current_time_gmt();
|
240 |
+
update_user_meta( $user->ID, $requirement['meta'], $evaluation );
|
241 |
+
|
242 |
+
if ( ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
|
243 |
+
continue;
|
244 |
+
}
|
245 |
+
|
246 |
+
$validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], array() );
|
247 |
+
|
248 |
+
if ( true === $validated ) {
|
249 |
+
continue;
|
250 |
+
}
|
251 |
+
|
252 |
+
ITSEC_Lib_Password_Requirements::flag_password_change_required( $user, $code );
|
253 |
+
}
|
254 |
+
|
255 |
+
update_user_meta( $user->ID, self::META_KEY, $config );
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* Validate password.
|
260 |
+
*
|
261 |
+
* @param \WP_Error $error
|
262 |
+
* @param \WP_User|stdClass $user
|
263 |
+
* @param string $new_password
|
264 |
+
* @param array $args
|
265 |
+
*/
|
266 |
+
public function validate_password( $error, $user, $new_password, $args ) {
|
267 |
+
|
268 |
+
$settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
|
269 |
+
|
270 |
+
foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
|
271 |
+
|
272 |
+
if ( ! $requirement['evaluate'] || ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
|
273 |
+
continue;
|
274 |
+
}
|
275 |
+
|
276 |
+
$evaluation = call_user_func( $requirement['evaluate'], $new_password, $user );
|
277 |
+
|
278 |
+
if ( is_wp_error( $evaluation ) ) {
|
279 |
+
continue;
|
280 |
+
}
|
281 |
+
|
282 |
+
$validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], $args );
|
283 |
+
|
284 |
+
if ( true === $validated ) {
|
285 |
+
continue;
|
286 |
+
}
|
287 |
+
|
288 |
+
// The default error message is a safeguard that should never occur.
|
289 |
+
$message = $validated ? $validated : esc_html__( "The provided password does not meet this site's requirements.", 'better-wp-security' );
|
290 |
+
|
291 |
+
switch ( $args['context'] ) {
|
292 |
+
case 'admin-user-create':
|
293 |
+
$message .= ' ' . __( 'The user has not been created.', 'better-wp-security' );
|
294 |
+
break;
|
295 |
+
case 'admin-profile-update':
|
296 |
+
$message .= ' ' . __( 'The user changes have not been saved.', 'better-wp-security' );
|
297 |
+
break;
|
298 |
+
case 'profile-update':
|
299 |
+
$message .= ' ' . __( 'Your profile has not been updated.', 'better-wp-security' );
|
300 |
+
break;
|
301 |
+
case 'reset-password':
|
302 |
+
$message .= ' ' . __( 'The password has not been updated.', 'better-wp-security' );
|
303 |
+
break;
|
304 |
+
}
|
305 |
+
|
306 |
+
$error->add( 'pass', $message );
|
307 |
+
}
|
308 |
+
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* When a user logs in, run any flag checks to see if a password change should be forced.
|
312 |
+
*
|
313 |
+
* @param string $username
|
314 |
+
* @param WP_User|null $user
|
315 |
+
*/
|
316 |
+
public function flag_check( $username, $user = null ) {
|
317 |
+
|
318 |
+
if ( ! $user && is_user_logged_in() ) {
|
319 |
+
$user = wp_get_current_user();
|
320 |
+
}
|
321 |
+
|
322 |
+
if ( ! $user instanceof WP_User || ! $user->exists() ) {
|
323 |
+
return;
|
324 |
+
}
|
325 |
+
|
326 |
+
foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
|
327 |
+
if ( ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( $code ) ) {
|
328 |
+
continue;
|
329 |
+
}
|
330 |
+
|
331 |
+
$settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( $code );
|
332 |
+
|
333 |
+
if ( $requirement['flag_check'] && call_user_func( $requirement['flag_check'], $user, $settings ) ) {
|
334 |
+
ITSEC_Lib_Password_Requirements::flag_password_change_required( $user, $code );
|
335 |
+
|
336 |
+
return;
|
337 |
+
}
|
338 |
+
}
|
339 |
+
}
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Is a given requirement enabled.
|
343 |
+
*
|
344 |
+
* @param string $requirement
|
345 |
+
*
|
346 |
+
* @return bool
|
347 |
+
*/
|
348 |
+
protected function is_requirement_enabled( $requirement ) {
|
349 |
+
|
350 |
+
$requirements = ITSEC_Lib_Password_Requirements::get_registered();
|
351 |
+
|
352 |
+
if ( ! isset( $requirements[ $requirement ] ) ) {
|
353 |
+
return false;
|
354 |
+
}
|
355 |
+
|
356 |
+
// If the requirement does not have any settings, than it is always enabled.
|
357 |
+
if ( null === $requirements[ $requirement ]['settings_config'] ) {
|
358 |
+
return true;
|
359 |
+
}
|
360 |
+
|
361 |
+
$enabled = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
|
362 |
+
|
363 |
+
if ( ! empty( $enabled[ $requirement ] ) ) {
|
364 |
+
return true;
|
365 |
+
}
|
366 |
+
|
367 |
+
return false;
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* When a user's role changes, clear all the evaluation times as evaluat
|
372 |
+
*
|
373 |
+
* @param int $user_id
|
374 |
+
*/
|
375 |
+
public function handle_role_change( $user_id ) {
|
376 |
+
|
377 |
+
$config = get_user_meta( $user_id, self::META_KEY, true );
|
378 |
+
|
379 |
+
if ( ! $config || ! is_array( $config ) ) {
|
380 |
+
return;
|
381 |
+
}
|
382 |
+
|
383 |
+
$config['evaluation_times'] = array();
|
384 |
+
|
385 |
+
update_user_meta( $user_id, self::META_KEY, $config );
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Register the password change interstitial.
|
390 |
+
*
|
391 |
+
* @param ITSEC_Lib_Login_Interstitial $lib
|
392 |
+
*/
|
393 |
+
public function register_interstitial( $lib ) {
|
394 |
+
$lib->register( 'update-password', array( $this, 'render_interstitial' ), array(
|
395 |
+
'show_to_user' => array( 'ITSEC_Lib_Password_Requirements', 'password_change_required' ),
|
396 |
+
'info_message' => array( 'ITSEC_Lib_Password_Requirements', 'get_message_for_password_change_reason' ),
|
397 |
+
'submit' => array( $this, 'submit' ),
|
398 |
+
) );
|
399 |
+
}
|
400 |
+
|
401 |
+
/**
|
402 |
+
* Render the interstitial.
|
403 |
+
*
|
404 |
+
* @param WP_User $user
|
405 |
+
*/
|
406 |
+
public function render_interstitial( $user ) {
|
407 |
+
do_action( 'itsec_password_requirements_change_form', $user );
|
408 |
+
?>
|
409 |
+
|
410 |
+
<div class="user-pass1-wrap">
|
411 |
+
<p><label for="pass1"><?php _e( 'New Password', 'better-wp-security' ); ?></label></p>
|
412 |
+
</div>
|
413 |
+
|
414 |
+
<div class="wp-pwd">
|
415 |
+
<span class="password-input-wrapper">
|
416 |
+
<input type="password" data-reveal="1"
|
417 |
+
data-pw="<?php echo esc_attr( wp_generate_password( 16 ) ); ?>" name="pass1" id="pass1"
|
418 |
+
class="input" size="20" value="" autocomplete="off" aria-describedby="pass-strength-result"/>
|
419 |
+
</span>
|
420 |
+
<div id="pass-strength-result" class="hide-if-no-js" aria-live="polite"><?php _e( 'Strength indicator', 'better-wp-security' ); ?></div>
|
421 |
+
<div class="pw-weak">
|
422 |
+
<label>
|
423 |
+
<input type="checkbox" name="pw_weak" class="pw-checkbox" />
|
424 |
+
<?php _e( 'Confirm use of weak password' ); ?>
|
425 |
+
</label>
|
426 |
+
</div>
|
427 |
+
</div>
|
428 |
+
|
429 |
+
<p class="user-pass2-wrap">
|
430 |
+
<label for="pass2"><?php _e( 'Confirm new password' ) ?></label><br/>
|
431 |
+
<input type="password" name="pass2" id="pass2" class="input" size="20" value="" autocomplete="off"/>
|
432 |
+
</p>
|
433 |
+
|
434 |
+
<p class="description indicator-hint"><?php echo wp_get_password_hint(); ?></p>
|
435 |
+
<br class="clear"/>
|
436 |
+
|
437 |
+
<p class="submit">
|
438 |
+
<input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="<?php esc_attr_e( 'Update Password', 'better-wp-security' ); ?>"/>
|
439 |
+
</p>
|
440 |
+
|
441 |
+
<?php
|
442 |
+
}
|
443 |
+
|
444 |
+
/**
|
445 |
+
* Handle the request to update the user's password.
|
446 |
+
*
|
447 |
+
* @param WP_User $user
|
448 |
+
* @param array $data POSTed data.
|
449 |
+
*
|
450 |
+
* @return WP_Error|null
|
451 |
+
*/
|
452 |
+
public function submit( $user, $data ) {
|
453 |
+
|
454 |
+
if ( empty( $data['pass1'] ) ) {
|
455 |
+
return new WP_Error(
|
456 |
+
'itsec-password-requirements-empty-password',
|
457 |
+
__( 'Please enter your new password.', 'better-wp-security' )
|
458 |
+
);
|
459 |
+
}
|
460 |
+
|
461 |
+
$error = ITSEC_Lib_Password_Requirements::validate_password( $user, $data['pass1'], array(
|
462 |
+
'context' => 'interstitial',
|
463 |
+
) );
|
464 |
+
|
465 |
+
if ( $error->get_error_message() ) {
|
466 |
+
return $error;
|
467 |
+
}
|
468 |
+
|
469 |
+
$error = wp_update_user( array(
|
470 |
+
'ID' => $user->ID,
|
471 |
+
'user_pass' => $data['pass1']
|
472 |
+
) );
|
473 |
+
|
474 |
+
if ( is_wp_error( $error ) ) {
|
475 |
+
return $error;
|
476 |
+
}
|
477 |
+
|
478 |
+
return null;
|
479 |
+
}
|
480 |
+
}
|
core/modules/password-requirements/css/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/password-requirements/css/settings-page.css
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
#itsec-module-card-password-requirements h4 {
|
2 |
+
margin: .25em 0;
|
3 |
+
}
|
core/modules/password-requirements/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php //You don't belong here. ?>
|
core/modules/password-requirements/js/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/password-requirements/js/settings-page.js
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function ( $ ) {
|
2 |
+
|
3 |
+
$( function () {
|
4 |
+
|
5 |
+
$( '.itsec-password-requirements-container' ).each( function () {
|
6 |
+
updateVisibility( $( this ).data( 'code' ) );
|
7 |
+
} );
|
8 |
+
|
9 |
+
$( '.itsec-password-requirements-container__enabled-wrap input[type="checkbox"]' ).on( 'change', function ( e ) {
|
10 |
+
updateVisibility( $( this ).parents( '.itsec-password-requirements-container' ).data( 'code' ) );
|
11 |
+
} )
|
12 |
+
} );
|
13 |
+
|
14 |
+
function updateVisibility( code ) {
|
15 |
+
var $checkbox = $( '.itsec-password-requirements-container__enabled-wrap--' + code + ' input[type="checkbox"]' ),
|
16 |
+
$details = $( '.itsec-password-requirements-container__settings-wrap--' + code );
|
17 |
+
|
18 |
+
if ( $checkbox.is( ':checked' ) ) {
|
19 |
+
$details.show();
|
20 |
+
} else {
|
21 |
+
$details.hide();
|
22 |
+
}
|
23 |
+
}
|
24 |
+
})( jQuery );
|
core/modules/password-requirements/settings-page.php
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class ITSEC_Password_Requirements_Settings_Page
|
5 |
+
*/
|
6 |
+
class ITSEC_Password_Requirements_Settings_Page extends ITSEC_Module_Settings_Page {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* ITSEC_Password_Requirements_Settings_Page constructor.
|
10 |
+
*/
|
11 |
+
public function __construct() {
|
12 |
+
$this->id = 'password-requirements';
|
13 |
+
$this->title = __( 'Password Requirements', 'better-wp-security' );
|
14 |
+
$this->description = __( 'Manage and configure Password Requirements for users.', 'better-wp-security' );
|
15 |
+
$this->can_save = true;
|
16 |
+
|
17 |
+
parent::__construct();
|
18 |
+
}
|
19 |
+
|
20 |
+
protected function render_description( $form ) {
|
21 |
+
?>
|
22 |
+
<p><?php esc_html_e( 'Manage and configure Password Requirements for users.', 'better-wp-security' ); ?></p>
|
23 |
+
<?php
|
24 |
+
}
|
25 |
+
|
26 |
+
public function enqueue_scripts_and_styles() {
|
27 |
+
wp_enqueue_script( 'itsec-password-requirements-settings-page', plugins_url( 'js/settings-page.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
|
28 |
+
wp_enqueue_style( 'itsec-password-requirements-settings-page', plugins_url( 'css/settings-page.css', __FILE__ ), array(), ITSEC_Core::get_plugin_build() );
|
29 |
+
|
30 |
+
do_action( 'itsec_password_requirements_enqueue_scripts_and_styles' );
|
31 |
+
}
|
32 |
+
|
33 |
+
public function handle_ajax_request( $data ) {
|
34 |
+
if ( ! isset( $data['password_requirement'] ) ) {
|
35 |
+
return;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Fires when Password Requirement ajax request is incoming.
|
40 |
+
*
|
41 |
+
* The dynamic portion of the hook, $data['password_requirement'] refers to the reason code of the requirement.
|
42 |
+
*
|
43 |
+
* @param array $data
|
44 |
+
*/
|
45 |
+
do_action( 'itsec_password_requirements_ajax_' . $data['password_requirement'], $data );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Render settings.
|
50 |
+
*
|
51 |
+
* @param ITSEC_Form $form
|
52 |
+
*/
|
53 |
+
protected function render_settings( $form ) {
|
54 |
+
|
55 |
+
$requirements = ITSEC_Lib_Password_Requirements::get_registered();
|
56 |
+
?>
|
57 |
+
|
58 |
+
<?php do_action( 'itsec_password_requirements_settings_before', $form ); ?>
|
59 |
+
|
60 |
+
<div class="itsec-password-requirements-settings">
|
61 |
+
<?php do_action( 'itsec_password_requirements_settings_begin', $form ); ?>
|
62 |
+
<?php foreach ( $requirements as $code => $requirement ):
|
63 |
+
|
64 |
+
if ( null === $requirement['settings_config'] ) {
|
65 |
+
continue;
|
66 |
+
}
|
67 |
+
|
68 |
+
$config = call_user_func( $requirement['settings_config'] );
|
69 |
+
|
70 |
+
$form->add_input_group( 'enabled_requirements' );
|
71 |
+
?>
|
72 |
+
<div class="itsec-settings-section itsec-password-requirements-container itsec-password-requirements-container--<?php echo esc_attr( $code ); ?>"
|
73 |
+
data-code="<?php echo esc_attr( $code ) ?>">
|
74 |
+
<h4><?php echo esc_html( isset( $config['label'] ) ? $config['label'] : $code ); ?></h4>
|
75 |
+
|
76 |
+
<?php if ( ! empty( $config['description'] ) ): ?>
|
77 |
+
<p class="description"><?php echo $config['description']; ?></p>
|
78 |
+
<?php endif; ?>
|
79 |
+
|
80 |
+
<table class="form-table">
|
81 |
+
<thead class="itsec-password-requirements-container__enabled-wrap itsec-password-requirements-container__enabled-wrap--<?php echo esc_attr( $code ); ?>">
|
82 |
+
<tr>
|
83 |
+
<th scope="row">
|
84 |
+
<label for="itsec-password-requirements-enabled_requirements-<?php echo esc_attr( $code ); ?>">
|
85 |
+
<?php esc_html_e( 'Enabled', 'better-wp-security' ); ?>
|
86 |
+
</label>
|
87 |
+
</th>
|
88 |
+
<td><?php $form->add_checkbox( $code ); ?></td>
|
89 |
+
</tr>
|
90 |
+
</thead>
|
91 |
+
<?php
|
92 |
+
$form->remove_input_group();
|
93 |
+
|
94 |
+
if ( ! empty( $config['render'] ) ) :
|
95 |
+
$form->add_input_group( 'requirement_settings', $code );
|
96 |
+
?>
|
97 |
+
<tbody class="itsec-password-requirements-container__settings-wrap itsec-password-requirements-container__settings-wrap--<?php echo esc_attr( $code ); ?>">
|
98 |
+
<?php call_user_func( $config['render'], $form ) ?>
|
99 |
+
</tbody>
|
100 |
+
<?php
|
101 |
+
$form->remove_input_group();
|
102 |
+
$form->remove_input_group();
|
103 |
+
endif; ?>
|
104 |
+
</table>
|
105 |
+
</div>
|
106 |
+
<?php endforeach; ?>
|
107 |
+
<?php do_action( 'itsec_password_requirements_settings_end', $form ); ?>
|
108 |
+
</div>
|
109 |
+
<?php do_action( 'itsec_password_requirements_settings_after', $form ); ?>
|
110 |
+
<?php
|
111 |
+
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
new ITSEC_Password_Requirements_Settings_Page();
|
core/modules/password-requirements/settings.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class ITSEC_Password_Requirements_Settings
|
5 |
+
*/
|
6 |
+
class ITSEC_Password_Requirements_Settings extends ITSEC_Settings {
|
7 |
+
|
8 |
+
public function get_id() {
|
9 |
+
return 'password-requirements';
|
10 |
+
}
|
11 |
+
|
12 |
+
public function get_defaults() {
|
13 |
+
return array(
|
14 |
+
'enabled_requirements' => array(),
|
15 |
+
'requirement_settings' => array(),
|
16 |
+
);
|
17 |
+
}
|
18 |
+
|
19 |
+
public function load() {
|
20 |
+
|
21 |
+
$this->settings = ITSEC_Storage::get( $this->get_id() );
|
22 |
+
$defaults = $this->get_defaults();
|
23 |
+
|
24 |
+
if ( ! is_array( $this->settings ) ) {
|
25 |
+
$this->settings = array();
|
26 |
+
}
|
27 |
+
|
28 |
+
$this->settings = array_merge( $defaults, $this->settings );
|
29 |
+
|
30 |
+
foreach ( ITSEC_Lib_Password_Requirements::get_registered() as $code => $requirement ) {
|
31 |
+
|
32 |
+
if ( null === $requirement['defaults'] ) {
|
33 |
+
continue;
|
34 |
+
}
|
35 |
+
|
36 |
+
if ( isset( $this->settings['requirement_settings'][ $code ] ) ) {
|
37 |
+
$current = $this->settings['requirement_settings'][ $code ];
|
38 |
+
} else {
|
39 |
+
$current = array();
|
40 |
+
}
|
41 |
+
|
42 |
+
$this->settings['requirement_settings'][ $code ] = wp_parse_args( $current, $requirement['defaults'] );
|
43 |
+
}
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
ITSEC_Modules::register_settings( new ITSEC_Password_Requirements_Settings() );
|
core/modules/password-requirements/validator.php
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class ITSEC_Password_Requirements_Validator
|
5 |
+
*/
|
6 |
+
class ITSEC_Password_Requirements_Validator extends ITSEC_Validator {
|
7 |
+
|
8 |
+
/** @var string */
|
9 |
+
private $current_requirement;
|
10 |
+
|
11 |
+
public function get_id() {
|
12 |
+
return 'password-requirements';
|
13 |
+
}
|
14 |
+
|
15 |
+
protected function sanitize_settings() {
|
16 |
+
$this->sanitize_setting( 'array', 'enabled_requirements', __( 'Enabled Requirements', 'better-wp-security' ) );
|
17 |
+
$this->sanitize_setting( 'array', 'requirement_settings', __( 'Requirement Settings', 'better-wp-security' ) );
|
18 |
+
|
19 |
+
$requirements = ITSEC_Lib_Password_Requirements::get_registered();
|
20 |
+
|
21 |
+
$settings = $this->settings;
|
22 |
+
|
23 |
+
foreach ( $requirements as $code => $requirement ) {
|
24 |
+
if ( null === $requirement['settings_config'] ) {
|
25 |
+
continue;
|
26 |
+
}
|
27 |
+
|
28 |
+
$config = call_user_func( $requirement['settings_config'] );
|
29 |
+
$sanitize = call_user_func( $config['sanitize'], $this->settings );
|
30 |
+
|
31 |
+
if ( is_wp_error( $sanitize ) ) {
|
32 |
+
$this->add_error( $sanitize );
|
33 |
+
|
34 |
+
if ( ITSEC_Core::is_interactive() ) {
|
35 |
+
$this->set_can_save( false );
|
36 |
+
}
|
37 |
+
} elseif ( is_array( $sanitize ) ) {
|
38 |
+
$this->settings = isset( $settings['requirement_settings'][ $code ] ) ? $settings['requirement_settings'][ $code ] : $requirement['defaults'];
|
39 |
+
$this->current_requirement = $code;
|
40 |
+
|
41 |
+
foreach ( $sanitize as $args ) {
|
42 |
+
call_user_func_array( array( $this, 'sanitize_setting' ), $args );
|
43 |
+
}
|
44 |
+
|
45 |
+
$settings['requirement_settings'][ $code ] = $this->settings;
|
46 |
+
$this->settings = $settings;
|
47 |
+
$this->current_requirement = null;
|
48 |
+
}
|
49 |
+
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
protected function generate_error( $id, $var, $type, $error ) {
|
54 |
+
if ( null === $this->current_requirement ) {
|
55 |
+
return parent::generate_error( $id, $var, $type, $error );
|
56 |
+
}
|
57 |
+
|
58 |
+
return new WP_Error( "itsec-validator-$id-invalid-type-enabled_requirements-{$this->current_requirement}-$var-$type", $error );
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
ITSEC_Modules::register_validator( new ITSEC_Password_Requirements_Validator() );
|
core/modules/pro/settings-page.php
CHANGED
@@ -36,22 +36,6 @@ final class ITSEC_Malware_Scheduling_Settings_Page extends ITSEC_Module_Settings
|
|
36 |
new ITSEC_Malware_Scheduling_Settings_Page();
|
37 |
|
38 |
|
39 |
-
final class ITSEC_Password_Expiration_Settings_Page extends ITSEC_Module_Settings_Page {
|
40 |
-
public function __construct() {
|
41 |
-
$this->id = 'password-expiration';
|
42 |
-
$this->title = __( 'Password Expiration', 'better-wp-security' );
|
43 |
-
$this->description = __( 'Strengthen the passwords on the site with automated password expiration.', 'better-wp-security' );
|
44 |
-
$this->type = 'recommended';
|
45 |
-
$this->pro = true;
|
46 |
-
$this->upsell = true;
|
47 |
-
$this->upsell_url = 'https://ithemes.com/security/wordpress-password-security/?utm_source=wordpressadmin&utm_medium=widget&utm_campaign=itsecfreecta';
|
48 |
-
|
49 |
-
parent::__construct();
|
50 |
-
}
|
51 |
-
}
|
52 |
-
new ITSEC_Password_Expiration_Settings_Page();
|
53 |
-
|
54 |
-
|
55 |
final class ITSEC_Privilege_Escalation_Settings_Page extends ITSEC_Module_Settings_Page {
|
56 |
public function __construct() {
|
57 |
$this->id = 'privilege';
|
36 |
new ITSEC_Malware_Scheduling_Settings_Page();
|
37 |
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
final class ITSEC_Privilege_Escalation_Settings_Page extends ITSEC_Module_Settings_Page {
|
40 |
public function __construct() {
|
41 |
$this->id = 'privilege';
|
core/modules/security-check/scanner.php
CHANGED
@@ -56,7 +56,7 @@ final class ITSEC_Security_Check_Scanner {
|
|
56 |
|
57 |
self::add_network_brute_force_signup();
|
58 |
|
59 |
-
self::
|
60 |
self::enforce_activation( 'two-factor', __( 'Two-Factor Authentication', 'better-wp-security' ) );
|
61 |
self::enforce_setting( 'two-factor', 'available_methods', 'all', esc_html__( 'Changed the Authentication Methods Available to Users setting in Two-Factor Authentication to "All Methods".', 'better-wp-security' ) );
|
62 |
self::enforce_setting( 'two-factor', 'protect_user_type', 'privileged_users', esc_html__( 'Changed the User Type Protection setting in Two-Factor Authentication to "Privileged Users".', 'better-wp-security' ) );
|
@@ -155,6 +155,21 @@ final class ITSEC_Security_Check_Scanner {
|
|
155 |
self::$feedback->add_text( sprintf( $text, $name ) );
|
156 |
}
|
157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
public static function activate_network_brute_force( $data ) {
|
159 |
if ( ! isset( $data['email'] ) ) {
|
160 |
ITSEC_Response::add_error( new WP_Error( 'itsec-security-check-missing-email', __( 'The email value is missing.', 'better-wp-security' ) ) );
|
56 |
|
57 |
self::add_network_brute_force_signup();
|
58 |
|
59 |
+
self::enforce_password_requirement_enabled( 'strength', __( 'Strong Password Enforcement', 'better-wp-security' ) );
|
60 |
self::enforce_activation( 'two-factor', __( 'Two-Factor Authentication', 'better-wp-security' ) );
|
61 |
self::enforce_setting( 'two-factor', 'available_methods', 'all', esc_html__( 'Changed the Authentication Methods Available to Users setting in Two-Factor Authentication to "All Methods".', 'better-wp-security' ) );
|
62 |
self::enforce_setting( 'two-factor', 'protect_user_type', 'privileged_users', esc_html__( 'Changed the User Type Protection setting in Two-Factor Authentication to "Privileged Users".', 'better-wp-security' ) );
|
155 |
self::$feedback->add_text( sprintf( $text, $name ) );
|
156 |
}
|
157 |
|
158 |
+
private static function enforce_password_requirement_enabled( $requirement, $description ) {
|
159 |
+
|
160 |
+
$active = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
|
161 |
+
|
162 |
+
if ( ! empty( $active[ $requirement ] ) ) {
|
163 |
+
return;
|
164 |
+
}
|
165 |
+
|
166 |
+
$active[ $requirement ] = true;
|
167 |
+
|
168 |
+
ITSEC_Modules::set_setting( 'password-requirements', 'enabled_requirements', $active );
|
169 |
+
self::$feedback->add_section( 'enforce-setting-password-requirements-enabled_requirements', array( 'status' => 'action-taken' ) );
|
170 |
+
self::$feedback->add_text( $description );
|
171 |
+
}
|
172 |
+
|
173 |
public static function activate_network_brute_force( $data ) {
|
174 |
if ( ! isset( $data['email'] ) ) {
|
175 |
ITSEC_Response::add_error( new WP_Error( 'itsec-security-check-missing-email', __( 'The email value is missing.', 'better-wp-security' ) ) );
|
core/modules/strong-passwords/class-itsec-strong-passwords.php
CHANGED
@@ -1,247 +1,218 @@
|
|
1 |
<?php
|
2 |
|
3 |
final class ITSEC_Strong_Passwords {
|
|
|
|
|
|
|
4 |
public function __construct() {
|
5 |
|
6 |
-
|
7 |
-
add_action( 'user_profile_update_errors', array( $this, 'filter_user_profile_update_errors' ), 0, 3 );
|
8 |
-
add_action( 'itsec_validate_password', array( $this, 'validate_password' ), 10, 4 );
|
9 |
|
10 |
add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts' ) );
|
11 |
-
add_action( '
|
|
|
12 |
}
|
13 |
|
14 |
/**
|
15 |
-
*
|
16 |
-
*
|
17 |
-
* @return void
|
18 |
*/
|
19 |
-
public function
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
}
|
22 |
|
23 |
/**
|
24 |
-
*
|
25 |
*
|
26 |
-
* @return
|
27 |
*/
|
28 |
-
public function
|
29 |
|
30 |
-
$
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Handle submission of a form to create or edit a user.
|
37 |
-
*
|
38 |
-
* @param WP_Error $errors WP_Error object.
|
39 |
-
* @param bool $update Whether this is a user update.
|
40 |
-
* @param stdClass $user User object.
|
41 |
-
*
|
42 |
-
* @return WP_Error
|
43 |
-
*/
|
44 |
-
public function filter_user_profile_update_errors( $errors, $update, $user ) {
|
45 |
-
|
46 |
-
// An error regarding the password was already found.
|
47 |
-
if ( $errors->get_error_data( 'pass' ) ) {
|
48 |
-
return $errors;
|
49 |
}
|
50 |
|
51 |
-
if (
|
52 |
-
return
|
53 |
}
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
$strength = get_user_meta( $user->ID, 'itsec-password-strength', true );
|
59 |
-
|
60 |
-
if ( ! is_numeric( $strength ) || $strength < 0 || $strength > 4 ) {
|
61 |
-
// Not enough data to determine whether a change of password is required.
|
62 |
-
return $errors;
|
63 |
-
}
|
64 |
|
65 |
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
|
66 |
|
67 |
-
if (
|
68 |
-
|
69 |
-
} else {
|
70 |
-
$role = ITSEC_Lib_Canonical_Roles::get_user_role( $user );
|
71 |
}
|
72 |
-
|
73 |
-
if ( ! $this->role_requires_strong_password( $role ) ) {
|
74 |
-
return $errors;
|
75 |
-
}
|
76 |
-
|
77 |
-
if ( 4 === (int) $strength ) {
|
78 |
-
return $errors;
|
79 |
-
}
|
80 |
-
|
81 |
-
if ( ! $update ) {
|
82 |
-
$context = 'admin-user-create';
|
83 |
-
} elseif ( $user->ID === get_current_user_id() ) {
|
84 |
-
$context = 'profile-update';
|
85 |
-
} else {
|
86 |
-
$context = 'admin-profile-update';
|
87 |
-
}
|
88 |
-
|
89 |
-
$errors->add( 'pass', $this->make_error_message( $context ) );
|
90 |
-
|
91 |
-
return $errors;
|
92 |
}
|
93 |
|
94 |
/**
|
95 |
-
*
|
|
|
|
|
96 |
*
|
97 |
-
* @param
|
98 |
-
* @param WP_User $user
|
99 |
-
* @param string $new_password
|
100 |
-
* @param array $args
|
101 |
*/
|
102 |
-
public function
|
103 |
|
104 |
-
if (
|
105 |
-
|
106 |
-
} else {
|
107 |
-
$reported_strength = false;
|
108 |
}
|
109 |
|
110 |
-
|
111 |
-
|
112 |
-
} elseif ( isset( $args['role'] ) ) {
|
113 |
-
$role = $this->get_canonical_role_from_role_and_user( $args['role'], $user );
|
114 |
-
} else {
|
115 |
-
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
|
116 |
-
$role = ITSEC_Lib_Canonical_Roles::get_user_role( $user );
|
117 |
-
}
|
118 |
|
119 |
-
|
120 |
-
return;
|
121 |
-
}
|
122 |
|
123 |
-
if (
|
124 |
-
|
125 |
}
|
|
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
}
|
131 |
|
132 |
/**
|
133 |
-
*
|
134 |
*
|
135 |
-
* @param string
|
136 |
* @param WP_User $user
|
137 |
*
|
138 |
-
* @return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
139 |
*/
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
$role_caps = array_keys( array_filter( wp_roles()->get_role( $role )->capabilities ) );
|
145 |
}
|
146 |
|
147 |
-
|
148 |
|
149 |
-
|
150 |
-
$wp_roles = wp_roles();
|
151 |
|
152 |
-
|
153 |
-
|
154 |
-
$user_caps[] = $has;
|
155 |
-
}
|
156 |
-
}
|
157 |
}
|
158 |
|
159 |
-
|
|
|
160 |
|
161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
}
|
163 |
|
164 |
/**
|
165 |
-
*
|
166 |
-
*
|
167 |
-
* @param string $context
|
168 |
*
|
169 |
-
* @
|
170 |
*/
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
185 |
}
|
186 |
|
187 |
/**
|
188 |
-
*
|
189 |
*
|
190 |
-
* @param
|
191 |
*
|
192 |
-
* @return
|
193 |
*/
|
194 |
-
|
195 |
-
|
|
|
|
|
|
|
|
|
196 |
|
197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
|
199 |
-
return
|
200 |
}
|
201 |
|
202 |
/**
|
203 |
-
*
|
204 |
*
|
205 |
-
* @param WP_User
|
206 |
-
*
|
207 |
-
* description. A member of user_pass is required if $password_strength is false.
|
208 |
-
* @param string $new_password The user's new password.
|
209 |
-
* @param int|boolean $password_strength [optional] An integer value representing the password strength, if known, or false.
|
210 |
-
* Defaults to false.
|
211 |
*
|
212 |
-
* @return
|
213 |
*/
|
214 |
-
private function
|
215 |
-
|
216 |
-
if ( false !== $password_strength ) {
|
217 |
-
return $password_strength < 4;
|
218 |
-
}
|
219 |
-
|
220 |
-
if ( ! empty( $_POST['password_strength'] ) && 'strong' !== $_POST['password_strength'] ) {
|
221 |
-
// We want to validate the password strength if the form data says that the password is strong since we want
|
222 |
-
// to protect against spoofing. If the form data says that the password isn't strong, believe it.
|
223 |
-
|
224 |
-
$password_strength = 1;
|
225 |
-
} else {
|
226 |
-
// The form data does not indicate a password strength or the data claimed that the password is strong,
|
227 |
-
// which is a claim that must be validated. Use the zxcvbn library to find the password strength score.
|
228 |
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
}
|
238 |
}
|
239 |
-
|
240 |
-
$results = ITSEC_Lib::get_password_strength_results( $new_password, $penalty_strings );
|
241 |
-
$password_strength = $results->score;
|
242 |
}
|
243 |
|
244 |
-
|
|
|
|
|
245 |
}
|
246 |
}
|
247 |
|
1 |
<?php
|
2 |
|
3 |
final class ITSEC_Strong_Passwords {
|
4 |
+
|
5 |
+
const STRENGTH_KEY = 'itsec-password-strength';
|
6 |
+
|
7 |
public function __construct() {
|
8 |
|
9 |
+
add_action( 'itsec_register_password_requirements', array( $this, 'register_requirements' ) );
|
|
|
|
|
10 |
|
11 |
add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts' ) );
|
12 |
+
add_action( 'resetpass_form', array( $this, 'add_scripts_to_wp_login' ) );
|
13 |
+
add_action( 'itsec_password_requirements_change_form', array( $this, 'add_scripts_to_wp_login' ) );
|
14 |
}
|
15 |
|
16 |
/**
|
17 |
+
* Register the Strong Passwords requirement.
|
|
|
|
|
18 |
*/
|
19 |
+
public function register_requirements() {
|
20 |
+
ITSEC_Lib_Password_Requirements::register( 'strength', array(
|
21 |
+
'evaluate' => array( $this, 'evaluate' ),
|
22 |
+
'validate' => array( $this, 'validate' ),
|
23 |
+
'reason' => array( $this, 'reason' ),
|
24 |
+
'meta' => self::STRENGTH_KEY,
|
25 |
+
'evaluate_if_not_enabled' => true,
|
26 |
+
'defaults' => array( 'role' => 'administrator' ),
|
27 |
+
'settings_config' => array( $this, 'get_settings_config' ),
|
28 |
+
) );
|
29 |
}
|
30 |
|
31 |
/**
|
32 |
+
* Enqueue script to hide the acknowledge weak password checkbox.
|
33 |
*
|
34 |
+
* @return void
|
35 |
*/
|
36 |
+
public function add_scripts() {
|
37 |
|
38 |
+
global $pagenow;
|
39 |
|
40 |
+
if ( 'profile.php' !== $pagenow ) {
|
41 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
}
|
43 |
|
44 |
+
if ( ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( 'strength' ) ) {
|
45 |
+
return;
|
46 |
}
|
47 |
|
48 |
+
$settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( 'strength' );
|
49 |
+
$role = isset( $settings['role'] ) ? $settings['role'] : 'administrator';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
|
51 |
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
|
52 |
|
53 |
+
if ( ITSEC_Lib_Canonical_Roles::is_user_at_least( $role ) ) {
|
54 |
+
wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
|
|
|
|
|
55 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
}
|
57 |
|
58 |
/**
|
59 |
+
* On the reset password and login interstitial form, render the Strong Passwords JS to hide the acknowledge weak password checkbox.
|
60 |
+
*
|
61 |
+
* We have to do this in these late actions so we have access to the correct user data.
|
62 |
*
|
63 |
+
* @param WP_User $user
|
|
|
|
|
|
|
64 |
*/
|
65 |
+
public function add_scripts_to_wp_login( $user ) {
|
66 |
|
67 |
+
if ( ! ITSEC_Lib_Password_Requirements::is_requirement_enabled( 'strength' ) ) {
|
68 |
+
return;
|
|
|
|
|
69 |
}
|
70 |
|
71 |
+
$settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( 'strength' );
|
72 |
+
$role = isset( $settings['role'] ) ? $settings['role'] : 'administrator';
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
|
|
|
|
|
75 |
|
76 |
+
if ( ITSEC_Lib_Canonical_Roles::is_user_at_least( $role, $user ) ) {
|
77 |
+
wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
|
78 |
}
|
79 |
+
}
|
80 |
|
81 |
+
/**
|
82 |
+
* Provide the reason string displayed to users on the change password form.
|
83 |
+
*
|
84 |
+
* @param $evaluation
|
85 |
+
*
|
86 |
+
* @return string
|
87 |
+
*/
|
88 |
+
public function reason( $evaluation ) {
|
89 |
+
return esc_html__( 'Due to site rules, a strong password is required for your account. Please choose a new password that rates as strong on the meter.', 'better-wp-security' );
|
90 |
}
|
91 |
|
92 |
/**
|
93 |
+
* Evaluate the strength of a password.
|
94 |
*
|
95 |
+
* @param string $password
|
96 |
* @param WP_User $user
|
97 |
*
|
98 |
+
* @return int
|
99 |
+
*/
|
100 |
+
public function evaluate( $password, $user ) {
|
101 |
+
return $this->get_password_strength( $user, $password );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Validate whether a password strength is acceptable for a given user.
|
106 |
+
*
|
107 |
+
* @param int $strength
|
108 |
+
* @param WP_User|stdClass $user
|
109 |
+
* @param array $settings
|
110 |
+
* @param array $args
|
111 |
+
*
|
112 |
+
* @return bool
|
113 |
*/
|
114 |
+
public function validate( $strength, $user, $settings, $args ) {
|
115 |
+
|
116 |
+
if ( (int) $strength === 4 ) {
|
117 |
+
return true;
|
|
|
118 |
}
|
119 |
|
120 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
|
121 |
|
122 |
+
$role = isset( $args['canonical'] ) ? $args['canonical'] : ITSEC_Lib_Canonical_Roles::get_user_role( $user );
|
|
|
123 |
|
124 |
+
if ( ! ITSEC_Lib_Canonical_Roles::is_canonical_role_at_least( $settings['role'], $role ) ) {
|
125 |
+
return true;
|
|
|
|
|
|
|
126 |
}
|
127 |
|
128 |
+
return $this->make_error_message();
|
129 |
+
}
|
130 |
|
131 |
+
public function get_settings_config() {
|
132 |
+
return array(
|
133 |
+
'label' => esc_html__( 'Strong Passwords', 'better-wp-security' ),
|
134 |
+
'description' => esc_html__( 'Force users to use strong passwords as rated by the WordPress password meter.', 'better-wp-security' ),
|
135 |
+
'render' => array( $this, 'render_settings' ),
|
136 |
+
'sanitize' => array( $this, 'sanitize_settings' ),
|
137 |
+
);
|
138 |
}
|
139 |
|
140 |
/**
|
141 |
+
* Render the Settings Page.
|
|
|
|
|
142 |
*
|
143 |
+
* @param ITSEC_Form $form
|
144 |
*/
|
145 |
+
public function render_settings( $form ) {
|
146 |
+
|
147 |
+
$href = 'http://codex.wordpress.org/Roles_and_Capabilities';
|
148 |
+
$link = '<a href="' . $href . '" target="_blank" rel="noopener noreferrer">' . $href . '</a>';
|
149 |
+
?>
|
150 |
+
<tr>
|
151 |
+
<th scope="row">
|
152 |
+
<label for="itsec-password-requirements-requirement_settings-strength-role">
|
153 |
+
<?php esc_html_e( 'Minimum Role', 'better-wp-security' ); ?>
|
154 |
+
</label>
|
155 |
+
</th>
|
156 |
+
<td>
|
157 |
+
<?php $form->add_canonical_roles( 'role' ); ?>
|
158 |
+
<br/>
|
159 |
+
<label for="itsec-password-requirements-requirement_settings-strength-role"><?php _e( 'Minimum role at which a user must choose a strong password.', 'better-wp-security' ); ?></label>
|
160 |
+
<p class="description"><?php printf( __( 'For more information on WordPress roles and capabilities please see %s.', 'better-wp-security' ), $link ); ?></p>
|
161 |
+
<p class="warningtext description"><?php _e( 'Warning: If your site invites public registrations setting the role too low may annoy your members.', 'better-wp-security' ); ?></p>
|
162 |
+
</td>
|
163 |
+
</tr>
|
164 |
+
<?php
|
165 |
}
|
166 |
|
167 |
/**
|
168 |
+
* Get a list of the sanitizer rules to apply.
|
169 |
*
|
170 |
+
* @param array $settings
|
171 |
*
|
172 |
+
* @return array
|
173 |
*/
|
174 |
+
public function sanitize_settings( $settings ) {
|
175 |
+
return array(
|
176 |
+
array( 'string', 'role', esc_html__( 'Minimum Role for Strong Passwords', 'better-wp-security' ) ),
|
177 |
+
array( 'canonical-roles', 'role', esc_html__( 'Minimum Role for Strong Passwords', 'better-wp-security' ) ),
|
178 |
+
);
|
179 |
+
}
|
180 |
|
181 |
+
/**
|
182 |
+
* Get the strong password error message according to the given context.
|
183 |
+
*
|
184 |
+
* @return string
|
185 |
+
*/
|
186 |
+
private function make_error_message() {
|
187 |
+
$message = __( '<strong>Error</strong>: Due to site rules, a strong password is required. Please choose a new password that rates as <strong>Strong</strong> on the meter.', 'better-wp-security' );
|
188 |
|
189 |
+
return wp_kses( $message, array( 'strong' => array() ) );
|
190 |
}
|
191 |
|
192 |
/**
|
193 |
+
* Calculate the strength of a password.
|
194 |
*
|
195 |
+
* @param WP_User $user
|
196 |
+
* @param string $password
|
|
|
|
|
|
|
|
|
197 |
*
|
198 |
+
* @return int
|
199 |
*/
|
200 |
+
private function get_password_strength( $user, $password ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
|
202 |
+
$penalty_strings = array(
|
203 |
+
get_site_option( 'admin_email' )
|
204 |
+
);
|
205 |
+
$user_properties = array( 'user_login', 'first_name', 'last_name', 'nickname', 'display_name', 'user_email', 'user_url', 'description' );
|
206 |
|
207 |
+
foreach ( $user_properties as $user_property ) {
|
208 |
+
if ( isset( $user->$user_property ) ) {
|
209 |
+
$penalty_strings[] = $user->$user_property;
|
|
|
210 |
}
|
|
|
|
|
|
|
211 |
}
|
212 |
|
213 |
+
$results = ITSEC_Lib::get_password_strength_results( $password, $penalty_strings );
|
214 |
+
|
215 |
+
return $results->score;
|
216 |
}
|
217 |
}
|
218 |
|
core/modules/strong-passwords/js/script.js
CHANGED
@@ -1,10 +1,3 @@
|
|
1 |
jQuery( document ).ready( function () {
|
2 |
-
|
3 |
-
jQuery( '#resetpassform, #your-profile, #createuser' ).submit( function () {
|
4 |
-
|
5 |
-
jQuery( '#submit, #createusersub, #resetpassform' ).append( '<input type="hidden" name="password_strength" id="password_strength" value="' + jQuery( '#pass-strength-result' ).attr( 'class' ).replace( /hide-if-no-js /, '' ) + '">' );
|
6 |
-
|
7 |
-
} );
|
8 |
-
|
9 |
jQuery( '.pw-weak' ).remove();
|
10 |
-
} );
|
1 |
jQuery( document ).ready( function () {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
jQuery( '.pw-weak' ).remove();
|
3 |
+
} );
|
core/modules/strong-passwords/settings-page.php
DELETED
@@ -1,51 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
final class ITSEC_Strong_Passwords_Settings_Page extends ITSEC_Module_Settings_Page {
|
4 |
-
private $script_version = 1;
|
5 |
-
|
6 |
-
|
7 |
-
public function __construct() {
|
8 |
-
$this->id = 'strong-passwords';
|
9 |
-
$this->title = __( 'Strong Password Enforcement', 'better-wp-security' );
|
10 |
-
$this->description = __( 'Force users to use strong passwords as rated by the WordPress password meter.', 'better-wp-security' );
|
11 |
-
$this->type = 'recommended';
|
12 |
-
|
13 |
-
parent::__construct();
|
14 |
-
}
|
15 |
-
|
16 |
-
protected function render_description( $form ) {
|
17 |
-
|
18 |
-
?>
|
19 |
-
<p><?php _e( 'Force users to use strong passwords as rated by the WordPress password meter.', 'better-wp-security' ); ?></p>
|
20 |
-
<?php
|
21 |
-
|
22 |
-
}
|
23 |
-
|
24 |
-
protected function render_settings( $form ) {
|
25 |
-
$roles = array(
|
26 |
-
'administrator' => translate_user_role( 'Administrator' ),
|
27 |
-
'editor' => translate_user_role( 'Editor' ),
|
28 |
-
'author' => translate_user_role( 'Author' ),
|
29 |
-
'contributor' => translate_user_role( 'Contributor' ),
|
30 |
-
'subscriber' => translate_user_role( 'Subscriber' ),
|
31 |
-
);
|
32 |
-
|
33 |
-
?>
|
34 |
-
<table class="form-table itsec-settings-section">
|
35 |
-
<tr>
|
36 |
-
<th scope="row"><label for="itsec-strong-passwords-role"><?php _e( 'Select Role for Strong Passwords', 'better-wp-security' ); ?></label></th>
|
37 |
-
<td>
|
38 |
-
<?php $form->add_select( 'role', $roles ); ?>
|
39 |
-
<br />
|
40 |
-
<label for="itsec-strong-passwords-role"><?php _e( 'Minimum role at which a user must choose a strong password.', 'better-wp-security' ); ?></label>
|
41 |
-
<p class="description"><?php printf( __( 'For more information on WordPress roles and capabilities please see <a href="%1$s" target="_blank" rel="noopener noreferrer">%1$s</a>.', 'better-wp-security' ), 'http://codex.wordpress.org/Roles_and_Capabilities' ); ?></p>
|
42 |
-
<p class="warningtext description"><?php _e( 'Warning: If your site invites public registrations setting the role too low may annoy your members.', 'better-wp-security' ); ?></p>
|
43 |
-
</td>
|
44 |
-
</tr>
|
45 |
-
</table>
|
46 |
-
<?php
|
47 |
-
|
48 |
-
}
|
49 |
-
}
|
50 |
-
|
51 |
-
new ITSEC_Strong_Passwords_Settings_Page();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/strong-passwords/setup.php
CHANGED
@@ -6,10 +6,10 @@ if ( ! class_exists( 'ITSEC_Strong_Passwords_Setup' ) ) {
|
|
6 |
|
7 |
public function __construct() {
|
8 |
|
9 |
-
add_action( 'itsec_modules_do_plugin_activation',
|
10 |
-
add_action( 'itsec_modules_do_plugin_deactivation', array( $this, 'execute_deactivate' )
|
11 |
-
add_action( 'itsec_modules_do_plugin_uninstall',
|
12 |
-
add_action( 'itsec_modules_do_plugin_upgrade',
|
13 |
|
14 |
}
|
15 |
|
@@ -86,6 +86,28 @@ if ( ! class_exists( 'ITSEC_Strong_Passwords_Setup' ) ) {
|
|
86 |
}
|
87 |
}
|
88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
}
|
90 |
|
91 |
}
|
6 |
|
7 |
public function __construct() {
|
8 |
|
9 |
+
add_action( 'itsec_modules_do_plugin_activation', array( $this, 'execute_activate' ) );
|
10 |
+
add_action( 'itsec_modules_do_plugin_deactivation', array( $this, 'execute_deactivate' ) );
|
11 |
+
add_action( 'itsec_modules_do_plugin_uninstall', array( $this, 'execute_uninstall' ) );
|
12 |
+
add_action( 'itsec_modules_do_plugin_upgrade', array( $this, 'execute_upgrade' ), null, 2 );
|
13 |
|
14 |
}
|
15 |
|
86 |
}
|
87 |
}
|
88 |
|
89 |
+
if ( $itsec_old_version < 4096 ) {
|
90 |
+
$active = get_site_option( 'itsec_active_modules', array() );
|
91 |
+
|
92 |
+
if ( ! empty( $active['strong-passwords'] ) ) {
|
93 |
+
$active_requirements = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
|
94 |
+
$active_requirements['strength'] = true;
|
95 |
+
ITSEC_Modules::set_setting( 'password-requirements', 'enabled_requirements', $active_requirements );
|
96 |
+
}
|
97 |
+
|
98 |
+
$requirement_settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
|
99 |
+
$requirement_settings['strength']['role'] = ITSEC_Modules::get_setting( 'strong-passwords', 'role', 'administrator' );
|
100 |
+
ITSEC_Modules::set_setting( 'password-requirements', 'requirement_settings', $requirement_settings );
|
101 |
+
|
102 |
+
unset( $active['strong-passwords'] );
|
103 |
+
|
104 |
+
// Need to do this directly to be able to remove a module from the list entirely.
|
105 |
+
if ( is_multisite() ) {
|
106 |
+
update_site_option( 'itsec_active_modules', $active );
|
107 |
+
} else {
|
108 |
+
update_option( 'itsec_active_modules', $active );
|
109 |
+
}
|
110 |
+
}
|
111 |
}
|
112 |
|
113 |
}
|
core/notify.php
CHANGED
@@ -124,9 +124,11 @@ class ITSEC_Notify {
|
|
124 |
|
125 |
$data_proxy = new ITSEC_Notify_Data_Proxy( $data );
|
126 |
|
127 |
-
$mail = $nc->mail();
|
128 |
$mail->add_header( $title, $banner_title );
|
|
|
129 |
$mail->add_info_box( sprintf( esc_html__( 'The following is a summary of security related activity on your site: %s', 'better-wp-security' ), '<b>' . $mail->get_display_url() . '</b>' ) );
|
|
|
130 |
|
131 |
$content = $mail->get_content();
|
132 |
|
@@ -203,10 +205,13 @@ class ITSEC_Notify {
|
|
203 |
'<a href="' . ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_logs_page_url() ) . '"><b>',
|
204 |
'</b></a>'
|
205 |
) );
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
|
|
|
|
|
|
210 |
|
211 |
$mail->add_footer();
|
212 |
|
124 |
|
125 |
$data_proxy = new ITSEC_Notify_Data_Proxy( $data );
|
126 |
|
127 |
+
$mail = $nc->mail( 'digest' );
|
128 |
$mail->add_header( $title, $banner_title );
|
129 |
+
$mail->start_group( 'intro' );
|
130 |
$mail->add_info_box( sprintf( esc_html__( 'The following is a summary of security related activity on your site: %s', 'better-wp-security' ), '<b>' . $mail->get_display_url() . '</b>' ) );
|
131 |
+
$mail->end_group();
|
132 |
|
133 |
$content = $mail->get_content();
|
134 |
|
205 |
'<a href="' . ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_logs_page_url() ) . '"><b>',
|
206 |
'</b></a>'
|
207 |
) );
|
208 |
+
|
209 |
+
if ( apply_filters( 'itsec_security_digest_include_security_check', true ) ) {
|
210 |
+
$mail->add_divider();
|
211 |
+
$mail->add_large_text( esc_html__( 'Is your site as secure as it could be?', 'better-wp-security' ) );
|
212 |
+
$mail->add_text( esc_html__( 'Ensure your site is using recommended settings and features with a security check.', 'better-wp-security' ) );
|
213 |
+
$mail->add_button( esc_html__( 'Run a Security Check ✓', 'better-wp-security' ), ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_security_check_page_url() ) );
|
214 |
+
}
|
215 |
|
216 |
$mail->add_footer();
|
217 |
|
history.txt
CHANGED
@@ -765,3 +765,20 @@
|
|
765 |
7.0.1 - 2018-05-25 - Chris Jean & Timothy Jacobs
|
766 |
Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
|
767 |
Bug Fix: Skip recovery if File Change storage is empty.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
765 |
7.0.1 - 2018-05-25 - Chris Jean & Timothy Jacobs
|
766 |
Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
|
767 |
Bug Fix: Skip recovery if File Change storage is empty.
|
768 |
+
7.0.2 - 2018-06-14 - Chris Jean & Timothy Jacobs
|
769 |
+
Enhancement: Add UI to cancel in progress File Scan.
|
770 |
+
Enhancement: Add basic admin debug page to help diagnosing and resolving issues. Particularly with the events.
|
771 |
+
Enhancement: Add debug settings JSON editor.
|
772 |
+
Enhancement: Continually evaluate password strength for users instead of only during registration.
|
773 |
+
Enhancement: Introduce Password Requirements module for managing and enforcing password requirements.
|
774 |
+
Bug Fix: Accessing password requirement settings would not resolve properly in some instances.
|
775 |
+
Bug Fix: Away Mode would not lock out users who were already logged-in during the "away" period.
|
776 |
+
Bug Fix: Enforce the Strong Passwords requirement during Security Check.
|
777 |
+
Bug Fix: Ensure scheduling lock is cleared by the Cron Scheduler when not proceeding with running events.
|
778 |
+
Bug Fix: If a password requirement has been disabled or is no longer available, don't consider the password as needing a change.
|
779 |
+
Bug Fix: Only hide "Acknowledge Weak Password" checkbox if the user was not allowed to use a weak password.
|
780 |
+
Bug Fix: Password strength would not be evaluated if password was set using custom PHP or CLI commands.
|
781 |
+
Bug Fix: Prevent File Change from getting stuck in an infinite rescheduling loop on the first step.
|
782 |
+
Bug Fix: Remove distributed storage table on uninstall.
|
783 |
+
Tweak: Don't write to the tracked files setting if the file hash has not changed.
|
784 |
+
Tweak: If no last password change date is recorded for the user, treat their registration date as the last change date.
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: ithemes, chrisjean, gerroald, mattdanner, timothyblynjacobs
|
|
3 |
Tags: security, security plugin, malware, hack, secure, block, SSL, admin, htaccess, lockdown, login, protect, protection, anti virus, attack, injection, login security, maintenance, permissions, prevention, authentication, administration, password, brute force, ban, permissions, bots, user agents, xml rpc, security log
|
4 |
Requires at least: 4.7
|
5 |
Tested up to: 4.9.6
|
6 |
-
Stable tag: 7.0.
|
7 |
Requires PHP: 5.2
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
@@ -189,6 +189,24 @@ Free support may be available with the help of the community in the <a href="htt
|
|
189 |
|
190 |
== Changelog ==
|
191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
= 7.0.1 =
|
193 |
* Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
|
194 |
* Bug Fix: Skip recovery if File Change storage is empty.
|
@@ -454,5 +472,5 @@ Free support may be available with the help of the community in the <a href="htt
|
|
454 |
|
455 |
== Upgrade Notice ==
|
456 |
|
457 |
-
= 7.0.
|
458 |
-
Version 7.0.
|
3 |
Tags: security, security plugin, malware, hack, secure, block, SSL, admin, htaccess, lockdown, login, protect, protection, anti virus, attack, injection, login security, maintenance, permissions, prevention, authentication, administration, password, brute force, ban, permissions, bots, user agents, xml rpc, security log
|
4 |
Requires at least: 4.7
|
5 |
Tested up to: 4.9.6
|
6 |
+
Stable tag: 7.0.2
|
7 |
Requires PHP: 5.2
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
189 |
|
190 |
== Changelog ==
|
191 |
|
192 |
+
= 7.0.2 =
|
193 |
+
* Enhancement: Add UI to cancel in progress File Scan.
|
194 |
+
* Enhancement: Add basic admin debug page to help diagnosing and resolving issues. Particularly with the events.
|
195 |
+
* Enhancement: Add debug settings JSON editor.
|
196 |
+
* Enhancement: Continually evaluate password strength for users instead of only during registration.
|
197 |
+
* Enhancement: Introduce Password Requirements module for managing and enforcing password requirements.
|
198 |
+
* Bug Fix: Accessing password requirement settings would not resolve properly in some instances.
|
199 |
+
* Bug Fix: Away Mode would not lock out users who were already logged-in during the "away" period.
|
200 |
+
* Bug Fix: Enforce the Strong Passwords requirement during Security Check.
|
201 |
+
* Bug Fix: Ensure scheduling lock is cleared by the Cron Scheduler when not proceeding with running events.
|
202 |
+
* Bug Fix: If a password requirement has been disabled or is no longer available, don't consider the password as needing a change.
|
203 |
+
* Bug Fix: Only hide "Acknowledge Weak Password" checkbox if the user was not allowed to use a weak password.
|
204 |
+
* Bug Fix: Password strength would not be evaluated if password was set using custom PHP or CLI commands.
|
205 |
+
* Bug Fix: Prevent File Change from getting stuck in an infinite rescheduling loop on the first step.
|
206 |
+
* Bug Fix: Remove distributed storage table on uninstall.
|
207 |
+
* Tweak: Don't write to the tracked files setting if the file hash has not changed.
|
208 |
+
* Tweak: If no last password change date is recorded for the user, treat their registration date as the last change date.
|
209 |
+
|
210 |
= 7.0.1 =
|
211 |
* Bug Fix: Fixed an "Uncaught Error: Call to undefined function esc_like()" error that could occur when exporting or erasing personal data.
|
212 |
* Bug Fix: Skip recovery if File Change storage is empty.
|
472 |
|
473 |
== Upgrade Notice ==
|
474 |
|
475 |
+
= 7.0.2 =
|
476 |
+
Version 7.0.2 contains important bug fixes and various enhancements. It is recommended for all users.
|