- review for secure functionality;

This commit is contained in:
Mykyta Synelnikov
2023-07-07 00:34:11 +03:00
parent 25aa40b1c2
commit 75e3ce9391
16 changed files with 1247 additions and 438 deletions
@@ -0,0 +1,79 @@
jQuery(document).on("ready", function(){
const scan_results_wrapper = jQuery(".um-secure-scan-results");
const scan_button_elem = jQuery(".um-secure-scan-content");
const scan_capabilities = jQuery("input[data-field_id^='banned_capabilities']");
var UM_Secure = {
init: function() {
scan_results_wrapper.css({
'margin-top': '10px',
'padding': '10px',
'padding-bottom': '10px',
'background-color': '#fff',
'display': 'block',
'max-height': '200px',
'height': '500px',
'overflow-y': 'scroll',
});
scan_button_elem.on("click", function(e){
UM_Secure.effect();
e.preventDefault();
var me = jQuery(this);
me.prop("disabled", true);
scan_results_wrapper.empty();
UM_Secure.log( wp.i18n.__( 'Scanning site..', 'ultimate-member' ) );
UM_Secure.ajax('');
});
scan_capabilities.on("change", function(){
scan_button_elem.attr('disabled', true );
scan_button_elem.after( ' <small style="color: red;">' + wp.i18n.__( 'You must save the settings before you can run the scan.', 'ultimate-member' ) + '</small>' );
scan_capabilities.off("change");
})
},
ajax: function( last_capability ) {
let checkedCaps = [];
let checkedCapsInputs = scan_results_wrapper.parents('.um-form-table').find('input[type="checkbox"][data-field_id^="banned_capabilities_"]:checked');
checkedCapsInputs.each(function (){
checkedCaps.push( jQuery(this).data('field_id').replace('banned_capabilities_', '') );
});
var request = {
nonce: um_admin_scripts.nonce,
capabilities: checkedCaps,
last_scanned_capability: last_capability,
};
wp.ajax.send('um_secure_scan_affected_users', {
data: request,
success: function (response) {
if ( ! response.completed ) {
UM_Secure.ajax( response.last_scanned_capability );
UM_Secure.log( response.message );
} else if ( response.completed ) {
scan_results_wrapper.empty();
UM_Secure.log( response.recommendations );
scan_results_wrapper.find('.current').removeClass('current');
scan_button_elem.removeAttr('disabled');
}
},
});
},
log: function( str ) {
scan_results_wrapper.find('.current').removeClass('current');
scan_results_wrapper.append( '<span class="current">' + str + '</span><br/>' );
},
effect: function() {
var blink = function(){
scan_results_wrapper.find(".current").fadeTo(100, 0.1).fadeTo(200, 1.0);
};
setInterval(blink, 1000);
}
};
UM_Secure.init();
});
+10 -6
View File
@@ -88,6 +88,11 @@ if ( ! class_exists( 'um\admin\Admin' ) ) {
add_filter( 'post_updated_messages', array( &$this, 'post_updated_messages' ) );
}
public function includes() {
$this->notices();
$this->secure();
}
function init_variables() {
$this->role_meta = apply_filters(
@@ -2056,13 +2061,12 @@ if ( ! class_exists( 'um\admin\Admin' ) ) {
return $parent_file;
}
/**
* @since 2.0
*
* @return core\Admin_Notices()
*/
function notices() {
public function notices() {
if ( empty( UM()->classes['admin_notices'] ) ) {
UM()->classes['admin_notices'] = new core\Admin_Notices();
}
@@ -2072,13 +2076,13 @@ if ( ! class_exists( 'um\admin\Admin' ) ) {
/**
* @since 2.6.8
*
* @return core\Secure
* @return Secure
*/
public function secure() {
if ( empty( UM()->classes['admin_secure'] ) ) {
UM()->classes['admin_secure'] = new core\Secure();
if ( empty( UM()->classes['um\admin\secure'] ) ) {
UM()->classes['um\admin\secure'] = new Secure();
}
return UM()->classes['admin_secure'];
return UM()->classes['um\admin\secure'];
}
}
}
@@ -1,18 +1,28 @@
<?php
namespace um\admin\core;
/**
* Usermeta which we use:
*
* um_user_blocked__metadata
* um_user_blocked
* um_user_blocked__timestamp
*
* um_secure_has_reset_password
* um_secure_has_reset_password__timestamp
*/
namespace um\admin;
use WP_User;
use WP_Session_Tokens;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\admin\core\Secure' ) ) {
if ( ! class_exists( 'um\admin\Secure' ) ) {
/**
* Class Secure
*
* @package um\admin\core
* @package um\admin
*
* @since 2.6.8
*/
@@ -37,6 +47,21 @@ if ( ! class_exists( 'um\admin\core\Secure' ) ) {
add_action( 'um_settings_before_save', array( $this, 'check_secure_changes' ) );
add_action( 'um_settings_save', array( $this, 'on_settings_save' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
add_action( 'wp_ajax_um_secure_scan_affected_users', array( $this, 'ajax_scanner' ) );
}
public function admin_scripts( $hook ) {
// phpcs:disable WordPress.Security.NonceVerification
if ( 'ultimate-member_page_um_options' !== $hook || ( isset( $_GET['tab'] ) && 'secure' !== $_GET['tab'] ) ) {
return;
}
// phpcs:enable WordPress.Security.NonceVerification
wp_register_script( 'um_admin_secure', UM()->admin_enqueue()->js_url . 'um-admin-secure.js', array( 'jquery' ), UM_VERSION, true );
wp_enqueue_script( 'um_admin_secure' );
}
/**
@@ -68,7 +93,7 @@ if ( ! class_exists( 'um\admin\core\Secure' ) ) {
if ( ! empty( $users ) ) {
foreach ( $users as $user_id ) {
// Get an instance of WP_User_Meta_Session_Tokens
$sessions_manager = \WP_Session_Tokens::get_instance( $user_id );
$sessions_manager = WP_Session_Tokens::get_instance( $user_id );
// Remove all the session for instance user.
$sessions_manager->destroy_all();
@@ -119,7 +144,7 @@ if ( ! class_exists( 'um\admin\core\Secure' ) ) {
// Delete blocked meta.
delete_user_meta( $user_id, 'um_user_blocked__metadata' );
delete_user_meta( $user_id, 'um_user_blocked' );
delete_user_meta( $user_id, 'um_user_blocked__datetime' );
delete_user_meta( $user_id, 'um_user_blocked__timestamp' );
// Don't need to reset a password.
update_user_meta( $user_id, 'um_secure_has_reset_password', true );
@@ -144,77 +169,49 @@ if ( ! class_exists( 'um\admin\core\Secure' ) ) {
$nonce = wp_create_nonce( 'um-secure-expire-session-nonce' );
$count_users = count_users();
/**
* UM hook
*
* @type filter
* @title um_secure_register_form_banned_capabilities
* @description Modify banned capabilities for Register forms
* @input_vars
* [{"var":"$capabilities","type":"array","desc":"WordPress Administratrive Capabilities"}]
* @change_log
* ["Since: 2.6.8"]
* @usage
* <?php add_filter( 'um_secure_register_form_banned_capabilities', 'function_name', 10, 1 ); ?>
* @example
* <?php
* add_filter( 'um_secure_register_form_banned_capabilities', 'my_banned_capabilities', 10, 1 );
* function my_banned_capabilities( $capabiities ) {
* // your code here
* $capabiities[ ] = 'read'; // rejects all users with `read` capabilitiy.
* return $capabiities;
* }
* ?>
*/
$banned_admin_capabilities = apply_filters(
'um_secure_register_form_banned_capabilities',
array(
'create_sites',
'delete_sites',
'manage_network',
'manage_sites',
'manage_network_users',
'manage_network_plugins',
'manage_network_themes',
'manage_network_options',
'upgrade_network',
'setup_network',
'activate_plugins',
'edit_dashboard',
'edit_theme_options',
'export',
'import',
'list_users',
'remove_users',
'switch_themes',
'customize',
'delete_site',
'update_core',
'update_plugins',
'update_themes',
'install_plugins',
'install_themes',
'delete_themes',
'delete_plugins',
'edit_plugins',
'edit_themes',
'edit_files',
'edit_users',
'add_users',
'create_users',
'delete_users',
'level_10',
'manage_options',
'promote_users',
)
);
$banned_capabilities = array();
$banned_capabilities = array();
$banned_admin_capabilities = UM()->common()->secure()->get_banned_capabilities_list();
foreach ( $banned_admin_capabilities as $cap ) {
$banned_capabilities[ $cap ] = $cap;
}
$disabled_capabilities = UM()->options()->get_default( 'banned_capabilities' );
$disabled_capabilities_text = '<strong>' . implode( '</strong>, <strong>', $disabled_capabilities ) . '</strong>';
$scanner_content = '<button class="button um-secure-scan-content">' . esc_html__( 'Scan Now', 'ultimate-member' ) . '</button>';
$scanner_content .= '<span class="um-secure-scan-results">';
$scanner_content .= esc_html__( 'Last scan:', 'ultimate-member' ) . ' ';
$scan_status = get_option( 'um_secure_scan_status' );
$last_scanned_time = get_option( 'um_secure_last_time_scanned' );
if ( ! empty( $last_scanned_time ) ) {
$scanner_content .= human_time_diff( strtotime( $last_scanned_time ), strtotime( current_time( 'mysql' ) ) ) . ' ' . esc_html__( 'ago', 'ultimate-member' );
if ( 'started' === $scan_status ) {
$scanner_content .= ' - ' . esc_html__( 'Not Completed.', 'ultimate-member' );
}
} else {
$scanner_content .= esc_html__( 'Not Scanned yet.', 'ultimate-member' );
}
$scanner_content .= '</span>';
$secure_fields = array(
array(
'id' => 'banned_capabilities',
'type' => 'multi_checkbox',
'multi' => true,
'columns' => 2,
'options_disabled' => $disabled_capabilities,
'options' => $banned_capabilities,
'label' => __( 'Banned Administrative Capabilities', 'ultimate-member' ),
// translators: %s are disabled default capabilities that are enabled by default.
'description' => sprintf( __( 'All the above are default Administrator & Super Admin capabilities. When someone tries to inject capabilities to the Account, Profile & Register forms submission, it will be flagged with this option. The %s capabilities are locked to ensure no users will be created with these capabilities.', 'ultimate-member' ), $disabled_capabilities_text ),
),
array(
'id' => 'secure_scan_affected_users',
'type' => 'info_text',
'label' => __( 'Scanner', 'ultimate-member' ),
'value' => $scanner_content,
'description' => __( 'Scan your site to check for vulnerabilities prior to Ultimate Member version 2.6.7 and get recommendations to secure your site.', 'ultimate-member' ),
),
array(
'id' => 'lock_register_forms',
'type' => 'checkbox',
@@ -241,28 +238,21 @@ if ( ! class_exists( 'um\admin\core\Secure' ) ) {
);
}
$disabled_capabilities = UM()->options()->get_default( 'banned_capabilities' );
$disabled_capabilities_text = '<strong>' . implode( '</strong>, <strong>', $disabled_capabilities ) . '</strong>';
$secure_fields = array_merge(
$secure_fields,
array(
array(
'id' => 'banned_capabilities',
'type' => 'multi_checkbox',
'multi' => true,
'columns' => 2,
'options_disabled' => UM()->options()->get_default( 'banned_capabilities' ),
'options' => $banned_capabilities,
'label' => __( 'Banned Administrative Capabilities', 'ultimate-member' ),
// translators: %s are disabled default capabilities that are enabled by default.
'description' => sprintf( __( 'All the above are default Administrator & Super Admin capabilities. When someone tries to inject capabilities to the Account, Profile & Register forms submission, it will be flagged with this option. The %s capabilities are locked to ensure no users will be created with these capabilities.', 'ultimate-member' ), $disabled_capabilities_text ),
'id' => 'secure_ban_admins_accounts',
'type' => 'checkbox',
'label' => __( 'Enable ban for administrative capabilities', 'ultimate-member' ),
'description' => __( ' When someone tries to inject capabilities to the Account, Profile & Register forms submission, it will be banned.', 'ultimate-member' ),
),
array(
'id' => 'secure_notify_admins_banned_accounts',
'type' => 'checkbox',
'label' => __( 'Notify Administrators', 'ultimate-member' ),
'description' => __( 'When enabled, All administrators will be notified when someone has suspicious activities in Profile & Register forms.', 'ultimate-member' ),
'conditional' => array( 'secure_ban_admins_accounts', '=', 1 ),
),
array(
'id' => 'secure_notify_admins_banned_accounts__interval',
@@ -303,7 +293,7 @@ if ( ! class_exists( 'um\admin\core\Secure' ) ) {
$is_blocked = um_user( 'um_user_blocked' );
$account_status = um_user( 'account_status' );
if ( ! empty( $is_blocked ) && in_array( $account_status, array( 'rejected', 'inactive' ), true ) ) {
$datetime = um_user( 'um_user_blocked__datetime' );
$datetime = um_user( 'um_user_blocked__timestamp' );
$val .= '<div><small>' . esc_html__( 'Blocked Due to Suspicious Activity', 'ultimate-member' ) . '</small></div>';
$nonce = wp_create_nonce( 'um-security-restore-account-nonce-' . $user_id );
$restore_account_url = admin_url( 'users.php?user_id=' . $user_id . '&um_secure_restore_account=1&_wpnonce=' . $nonce );
+62 -1
View File
@@ -61,6 +61,8 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
//$this->future_changed();
$this->common_secure();
/**
* UM hook
*
@@ -522,7 +524,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
$messages[1]['content'] = __( 'Other users have been updated.', 'ultimate-member' );
break;
case 'um_secure_expire_sessions':
$messages[0]['content'] = __( 'All users sessions have been expired.', 'ultimate-member' );
$messages[0]['content'] = __( 'All users sessions have been successfully destroyed.', 'ultimate-member' );
break;
case 'um_secure_restore':
$messages[0]['content'] = __( 'Account has been successfully restored.', 'ultimate-member' );
@@ -786,6 +788,65 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
);
}
public function common_secure() {
if ( UM()->options()->get( 'lock_register_forms' ) ) {
ob_start();
?>
<p>
<?php esc_html_e( 'Your Register forms are now locked. You can unlock them in Ultimate Member > Settings > Secure > Lock All Register Forms.', 'ultimate-member' ); ?>
</p>
<?php
$message = ob_get_clean();
$this->add_notice(
'common_secure_register',
array(
'class' => 'warning',
'message' => $message,
'dismissible' => true,
),
1
);
}
if ( UM()->options()->get( 'display_login_form_notice' ) ) {
ob_start();
?>
<p>
<?php esc_html_e( 'Mandatory password changes has been enabled. You can disable them in Ultimate Member > Settings > Secure > Display Login form notice to reset passwords.', 'ultimate-member' ); ?>
</p>
<?php
$message = ob_get_clean();
$this->add_notice(
'common_secure_password_reset',
array(
'class' => 'warning',
'message' => $message,
'dismissible' => true,
),
1
);
}
if ( UM()->options()->get( 'secure_ban_admins_accounts' ) ) {
ob_start();
?>
<p>
<?php esc_html_e( 'Ban for administrative capabilities is enabled. You can disable them in Ultimate Member > Settings > Secure > Enable ban for administrative capabilities.', 'ultimate-member' ); ?>
</p>
<?php
$message = ob_get_clean();
$this->add_notice(
'common_secure_suspicious_activity',
array(
'class' => 'warning',
'message' => $message,
'dismissible' => true,
),
1
);
}
}
public function dismiss_notice() {
UM()->admin()->check_ajax_nonce();
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace um\ajax;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\ajax\Init' ) ) {
/**
* Class Init
*
* @package um\ajax
*/
class Init {
/**
* Create classes' instances where __construct isn't empty for hooks init
*
* @used-by \UM::includes()
*/
public function includes() {
$this->secure();
}
/**
* @since 2.6.8
*
* @return Secure
*/
public function secure() {
if ( empty( UM()->classes['um\ajax\secure'] ) ) {
UM()->classes['um\ajax\secure'] = new Secure();
}
return UM()->classes['um\ajax\secure'];
}
}
}
+410
View File
@@ -0,0 +1,410 @@
<?php
namespace um\ajax;
use WP_Session_Tokens;
use WP_User_Query;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class Secure
*
* @package um\ajax
*
* @since 2.6.8
*/
class Secure {
/**
* Secure constructor.
*
* @since 2.6.8
*/
public function __construct() {
add_action( 'wp_ajax_um_secure_scan_affected_users', array( $this, 'ajax_scanner' ) );
}
/**
* Scan affected users
*/
public function ajax_scanner() {
if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'um-admin-nonce' ) || ! current_user_can( 'manage_options' ) ) {
wp_die( esc_attr__( 'Security Check', 'ultimate-member' ) );
}
$last_scanned_capability = sanitize_key( $_REQUEST['last_scanned_capability'] );
if ( empty( $last_scanned_capability ) ) {
delete_option( 'um_secure_scanned_details' );
update_option( 'um_secure_scan_status', 'started' );
update_option( 'um_secure_last_time_scanned', current_time( 'mysql' ) );
}
$scan_details = get_option( 'um_secure_scanned_details' );
if ( ! empty( $_REQUEST['capabilities'] ) ) {
$request_capabilities = is_array( $_REQUEST['capabilities'] ) ? $_REQUEST['capabilities'] : array( $_REQUEST['capabilities'] );
$arr_banned_caps = array_map( 'sanitize_key', $request_capabilities );
} else {
$arr_banned_caps = UM()->options()->get( 'banned_capabilities' );
}
$proceed = false;
$completed = false;
$message = '';
$scanned_cap = '';
$user_affected = false;
$count_users = 0;
$last_element = end( $arr_banned_caps );
foreach ( $arr_banned_caps as $cap ) {
if ( empty( $last_scanned_capability ) ) {
$proceed = true;
} else {
if ( $last_scanned_capability === $cap ) { // if this was the last capability, skip this and proceed the next loop.
$proceed = true;
continue;
}
}
if ( ! $proceed ) {
continue;
}
$args = array(
'capability' => $cap,
'role__not_in' => array( 'administrator' ),
'fields' => 'ids',
);
$wp_user_query = new WP_User_Query( $args );
$count_users = $wp_user_query->get_total();
if ( $count_users <= 0 ) {
$message = '<strong>`' . esc_html( $cap ) . '`</strong> <span style="color:green">' . esc_html__( ' is safe ' ) . '</span>';
} else {
$user_affected = true;
$message = '<strong>`' . esc_html( $cap ) . '`</strong> <span style="color:red">' . sprintf( /* translators: has affected %d user account */ _n( 'has affected %d user account.', ' has affected %d user accounts.', $count_users, 'ultimate-member' ), $count_users ) . '</span>';
}
if ( $last_element === $cap ) {
$completed = true;
update_option( 'um_secure_scan_status', 'completed' );
}
$scanned_cap = $cap;
break;
}
if ( $user_affected ) {
$scan_details['affected_caps'][] = $scanned_cap;
$scan_details['scanned_caps'][ $scanned_cap ] = array(
'total_affected_users' => $count_users,
'users' => $wp_user_query->get_results(),
);
if ( ! isset( $scan_details['scanned_caps']['total_all_cap_flagged'] ) ) {
$scan_details['scanned_caps']['total_all_cap_flagged'] = 1;
} else {
++$scan_details['scanned_caps']['total_all_cap_flagged'];
}
}
if ( ! isset( $scan_details['scanned_caps']['total_all_affected_users'] ) ) {
$scan_details['scanned_caps']['total_all_affected_users'] = absint( $count_users );
} else {
$scan_details['scanned_caps']['total_all_affected_users'] = absint( $scan_details['scanned_caps']['total_all_affected_users'] ) + absint( $count_users );
}
update_option( 'um_secure_scanned_details', $scan_details );
wp_send_json_success(
array(
'last_scanned_capability' => $scanned_cap,
'message' => $message,
'completed' => $completed,
'recommendations' => $completed ? wp_kses( $this->scan_recommendations(), UM()->get_allowed_html( 'templates' ) ) : '',
)
);
}
/**
* Recommendations after the scan completed.
*
* @return string
*/
public function scan_recommendations() {
global $wp_roles, $wpdb;
$br = '</br>';
$check = '<span class="dashicons dashicons-yes-alt" style="color:green"></span>';
$flag = '<span class="dashicons dashicons-flag" style="color:red"></span>';
$warning = '<span class="dashicons dashicons-warning" style="color:red"></span>';
$all_plugins = get_plugins();
$active_plugins = apply_filters( 'active_plugins', get_option( 'active_plugins' ) );
$content = '-----' . $br . $br;
$suspicious_accounts = new WP_User_Query(
array(
'relation' => 'AND',
'number' => -1,
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'submitted',
'value' => sprintf( ':"%s";', $wpdb->prefix . '_capabilities' ),
'compare' => 'LIKE',
),
array(
'key' => 'submitted',
'value' => sprintf( ':"%s";', $wpdb->prefix . '_user_level' ),
'compare' => 'LIKE',
),
array(
'key' => 'submitted',
'value' => sprintf( '.*%s";', '_capabilities' ),
'compare' => 'REGEXP',
),
array(
'key' => 'submitted',
'value' => sprintf( '.*%s";', '_user_level' ),
'compare' => 'LIKE',
),
),
)
);
$suspicious_accounts_count = $suspicious_accounts->get_total();
$susp_accounts = $suspicious_accounts->get_results();
/**
* Disable and Kickout Suspicious accounts.
*/
if ( $suspicious_accounts_count > 0 ) {
$arr_might_lookout_accounts = array();
$arr_dates_registered = array();
$arr_suspected_accounts = array();
if ( ! empty( $susp_accounts ) ) {
foreach ( $susp_accounts as $user ) {
$arr_suspected_accounts[] = $user->ID;
$arr_dates_registered[] = strtotime( $user->user_registered );
if ( $user->__get( 'um_user_blocked' ) ) {
continue;
}
UM()->common()->secure()->revoke_caps( $user );
// Get an instance of WP_User_Meta_Session_Tokens
$sessions_manager = WP_Session_Tokens::get_instance( $user->ID );
// Remove all the session data for all users.
$sessions_manager->destroy_all();
}
}
$date_query = array();
$oldest_date = min( $arr_dates_registered );
$newest_date = max( $arr_dates_registered );
$might_affected_users = new WP_User_Query(
array(
'number' => -1,
'relation' => 'AND',
'date_query' => array(
'after' => human_time_diff( $oldest_date, strtotime( current_time( 'mysql' ) ) ) . ' ago',
),
)
);
}
$content .= '<span style="font-size:19px;padding-bottom:10px;display:block;border-bottom:1px solid #ccc;" id="um-secure-scanner-complete">' . ( $suspicious_accounts_count > 0 ? $warning : $check ) . __( 'Scan Complete.', 'ultimate-member' ) . '</span>';
$option = get_option( 'um_secure_scanned_details', $susp_accounts );
$scan_details = $option['scanned_caps'];
if ( $suspicious_accounts_count > 0 ) {
update_option( 'um_secure_found_suspicious_accounts', true );
$content .= $br . $flag . '<strong>Suspcious Accounts Detected!</strong> <br/>';
$content .= $br . __( 'We have found ', 'ultimate-member' ) . '<strong style="color:red;">' . /* translators: %s suspcious account */ sprintf( _n( '%s suspcious account', '%s suspcious accounts', $suspicious_accounts_count, 'ultimate-member' ), $suspicious_accounts_count ) . '</strong> ' . __( 'created on your site via Ultimate Member Forms.', 'ultimate-member' );
$content .= $br . __( 'We\'ve temporarily disabled the suspcious account(s) for you to <strong>take actions</strong>.', 'ultimate-member' );
if ( $might_affected_users->get_total() > 0 ) {
$od = gmdate( 'F m, Y', $oldest_date );
$nd = gmdate( 'F m, Y', $newest_date );
if ( $od !== $nd ) {
$date_registered = $od . ' to ' . $nd;
} else {
$date_registered = $od;
}
$content .= $br . $br . __( 'Also, We\'ve found ', 'ultimate-member' ) . '<strong style="color:red;">' . /* translators: %s suspcious account */ sprintf( _n( '%s account', '%s accounts', $might_affected_users->get_total(), 'ultimate-member' ), $might_affected_users->get_total() ) . '</strong> ' . sprintf( _n( 'created on %s when the suspicious account was created.', 'created on %s when the suspicious accounts were created.', $suspicious_accounts_count, 'ultimate-member' ), $date_registered );
}
} else {
$content .= $br . '<strong>Suspcious Accounts</strong> <br/>';
$content .= $br . $check . ' No suspicious accounts found. <br/>';
}
$content .= $br . $br . __( 'PLEASE READ OUR <strong>RECOMMENDATIONS</strong> BELOW: ', 'ultimate-member' ) . $br;
$content .= $br . '<div style="padding:10px; border:1px solid #ccc;"><strong style="color:red">WARNING:</strong> Ensure that you\'ve created a full backup of your site as your restoration point before changing anything on your site with our recommendations.</div>';
if ( $suspicious_accounts_count > 0 ) {
$lock_register_forms_url = admin_url( 'admin.php?page=um_options&tab=secure&um_secure_lock_register_forms=1&_wpnonce=' . wp_create_nonce( 'um_secure_lock_register_forms' ) );
$content .= $br . '1. Please temporarily lock all your active Register forms. <a href="' . esc_attr( $lock_register_forms_url ) . '" target="_blank">Click here to lock them now.</a> You can unblock the Register forms later. Just go to Ultimate Member > Settings > Secure > uncheck the option "Lock All Register Forms".';
$content .= $br . $br;
$suspicious_accounts_url = admin_url( 'users.php?um_status=inactive' );
$content .= '2. Review all suspicious accounts and delete them completely. <a href="' . esc_attr( $suspicious_accounts_url ) . '" target="_blank">Click here to review accounts.</a>';
$content .= $br . $br;
$nonce = wp_create_nonce( 'um-secure-expire-session-nonce' );
$destroy_all_sessions_url = admin_url( '?um_secure_expire_all_sessions=1&_wpnonce=' . esc_attr( $nonce ) . '&except_me=1' );
$content .= '3. If accounts are suspicious to you, please destroy all user sessions to logout active users on your site. <a href="' . esc_attr( $destroy_all_sessions_url ) . '" target="_blanl">Click here to Destroy Sessions now</a>';
$content .= $br . $br;
$content .= '4. Run a complete scan on your site using third-party Security plugins such as <a target="_blank" href="' . esc_attr( admin_url( 'plugin-install.php?s=Jetpack%2520Protect%2520WP%2520Scan&tab=search&type=term' ) ) . '">WPScan/Jetpack Protect or WordFence Security</a>.';
$content .= $br . $br;
$nonce = wp_create_nonce( 'um-secure-enable-reset-pass-nonce' );
$reset_pass_sessions_url = admin_url( '?um_secure_enable_reset_password=1&_wpnonce=' . esc_attr( $nonce ) . '&except_me=1' );
$content .= '5. Force users to Reset their Passwords. <a target="_blank" href="' . esc_attr( $reset_pass_sessions_url ) . '">Click here to enable this option</a>. When this option is enabled, users will be asked to reset their passwords(one-time) on the next login in the UM Login form.';
$content .= $br . $br;
$content .= '6. Once your site is secured, please create or enable Daily Backups of your server/site. You can contact your hosting provider to assist you on this matter.';
$content .= $br . $br;
$content .= '👇 Read more Recommendations below.';
$content .= $br;
}
if ( get_option( 'users_can_register' ) ) {
$content .= $br . $flag . '<strong>Default WP Register Form is Enabled</strong>';
$content .= $br . 'The default WordPress Register form is enabled. If you\'re getting Spam User Registrations, we recommend that you enable a Challenge-Response plugin such as our <a href="https://wordpress.org/plugins/um-recaptcha/" target="_blank">Ultimate Member - ReCaptcha</a> extension.';
$content .= $br;
}
$content .= $br . '<strong>Block Disposable Email Addresses/Domains</strong>';
if ( empty( UM()->options()->get( 'blocked_emails' ) ) ) {
$content .= $br . $flag . 'You are not blocking email addresses or disposable email domains that are mostly used for Spam Account Registrations. You can get the list of disposable email domains from <a href="https://github.com/champsupertramp/disposable-email-domains/blob/master/um_disposable_email_blocklist.txt" target="_blank">this repository</a> and then add them to <a target="_blank" href="' . esc_attr( 'admin.php?page=um_options&tab=access&section=other' ) . '">Blocked Email Addresses</a> options.';
$content .= $br;
} else {
$content .= $br . 'The default WordPress Register form is enabled. If you\'re getting Spam User Registrations, we recommend that you enable a Challenge-Response plugin such as our <a href="https://wordpress.org/plugins/um-recaptcha/" target="_blank">Ultimate Member - ReCaptcha</a> extension.';
$content .= $br;
}
$content .= $br . '<strong>Manage User Roles & Capabilities</strong> <br/>';
if ( absint( $scan_details['total_all_affected_users'] ) > 0 ) {
$count_flagged_caps = $scan_details['total_all_cap_flagged'];
$count_users = $scan_details['total_all_affected_users'];
$affected_caps = $option['affected_caps'];
$affected_roles = array();
$all_roles = $wp_roles->roles;
$editable_roles = apply_filters( 'editable_roles', $all_roles );
foreach ( $affected_caps as $cap ) {
foreach ( $editable_roles as $role_key => $role ) {
if ( in_array( $cap, array_keys( $role['capabilities'] ), true ) ) {
$affected_roles[ $role_key ] = $role['name'];
}
}
}
$content .= $br . $flag . 'We have found ' . sprintf( /* translators: */ _n( ' %d user account', ' %d user accounts ', $count_users, 'ultimate-member' ), $count_users );
$content .= sprintf( /* translators: */ _n( ' affected by %d capability selected in the Banned Administrative Capabilities.', ' affected by one of the %d capabilities selected in the Banned Administrative Capabilities.', $count_flagged_caps, 'ultimate-member' ), $count_flagged_caps );
$content .= $br . '- ' . implode( '<br/> - ', $affected_caps );
$content .= $br . $br . 'The flagged capabilities are related to the following roles: ' . $br . ' - ' . implode( '<br/> - ', array_values( $affected_roles ) );
$content .= $br . $br . 'The affected user accounts will be flagged as suspicious when they update their Profile/Account. If you are not using these capabilities, you may remove them from the roles in the <a target="_blank" href="' . admin_url( 'admin.php?page=um_roles' ) . '">User Role settings</a>. If the roles are not created via Ultimate Member > User Roles, you can use a <a href="' . admin_url( 'plugin-install.php?s=User%2520Role%2520Editor%2520WordPress%2520&tab=search&type=term' ) . '" target="_blank">third-party plugin</a> to modify the role capability.';
$content .= $br . $br . 'We strongly recommend that you never assign roles with the same capabilities as your administrators for your members/users and that may allow them to access the admin-side features and functionalities of your WordPress site.';
} else {
$content .= $check . 'Roles & Capabilities are all secured. No users are using the same capabilities as your administrators.';
}
$content .= $br . $br . '<strong>Require Strong Passwords</strong>';
if ( ! UM()->options()->get( 'require_strongpass' ) ) {
$content .= $br . $flag . 'We recommend that you enable and require "Strong Password" feature for all the Register, Reset Password & Account forms.';
$content .= $br . ' <a href="' . admin_url( 'admin.php?page=um_options&section=users' ) . '" target="_blank" >Click here to enable.</a>';
} else {
$content .= $br . $check . 'Your forms are already configured to require of using strong passwords.';
}
$content .= $br . $br . '<strong>Secure Site\'s Connection</strong>';
if ( ! isset( $_SERVER['HTTPS'] ) || 'on' !== $_SERVER['HTTPS'] ) {
$content .= $br . $flag . 'Your site cannot provide a secure connection. Please contact your hosting provider to enable SSL certifications on your server.';
}
$content .= $br . $br . '<strong>Install Challenge-Response plugin to Login & Register Forms</strong>';
if ( ! array_key_exists( 'um-recaptcha/um-recaptcha.php', $all_plugins ) ) {
if ( ! isset( $_SERVER['HTTPS'] ) || 'on' !== $_SERVER['HTTPS'] ) {
$content .= $br . $flag . 'We recommend that you install and enable ReCaptcha to Login & Register forms.';
}
} else {
if ( in_array( 'um-recaptcha/um-recaptcha.php', $active_plugins, true ) ) {
$content .= $br . $check . 'Ultimate Member ReCaptcha is actived.';
$um_forms = get_posts( 'post_type=um_form&numberposts=-1&fields=ids' );
foreach ( $um_forms as $fid ) {
switch ( get_post_meta( $fid, '_um_mode', true ) ) {
case 'register':
$has_captcha = absint( get_post_meta( $fid, '_um_register_g_recaptcha_status', true ) );
$content .= $br . '&nbsp;&nbsp;- Register: <a target="_blank" href="' . get_edit_post_link( $fid ) . '">' . get_the_title( $fid ) . '</a> recaptcha ' . ( 1 === $has_captcha ? ' is <strong>enabled</strong> ' . $check : 'is <strong>disabled</strong> ' . $flag );
break;
case 'login':
$has_captcha = absint( get_post_meta( $fid, '_um_login_g_recaptcha_status', true ) );
$content .= $br . '&nbsp;&nbsp;- Login: <a target="_blank" href="' . get_edit_post_link( $fid ) . '">' . get_the_title( $fid ) . '</a> recaptcha ' . ( 1 === $has_captcha ? ' is <strong>enabled</strong> ' . $check : 'is <strong>disabled</strong> ' . $flag );
break;
}
}
$reset_pass_form = absint( UM()->options()->get( 'g_recaptcha_password_reset' ) );
$content .= $br . '&nbsp;&nbsp;- Reset Password Form\'s recaptcha ' . ( 1 === $reset_pass_form ? ' is <strong>enabled</strong> ' . $check : 'is <strong>disabled</strong> ' . $flag );
} elseif ( array_key_exists( 'um-recaptcha/um-recaptcha.php', $all_plugins ) ) {
$content .= $br . $flag . 'Ultimate Member ReCaptcha is installed but not activated.';
} else {
$content .= $br . $flag . 'We recommend that you install and enable <a href="https://wordpress.org/plugins/um-recaptcha/" target="_blank">ReCaptcha</a> to Login & Register forms.';
}
}
$update_plugins = get_site_transient( 'update_plugins' );
$update_themes = get_site_transient( 'update_themes' );
$update_wp_core = get_site_transient( 'update_core' );
global $wp_version;
$content .= $br . $br . '<strong>Keep Themes & Plugins up to date.</strong>';
$content .= $br . __( 'It is important that you update your themes/plugins if the theme/plugin creators update is aimed at fixing security, bug and vulnerability issues. It is not a good idea to ignore available updates as this may give hackers an advantage when trying to access your website.', 'ultimate-member' );
if ( isset( $update_plugins->response ) && ! empty( $update_plugins->response ) ) {
$content .= $br . $br . $flag . sprintf( /* translators: */ _n( 'There\'s %d plugin that requires an update.', 'There are %d plugins that require updates', count( $update_plugins->response ), 'ultimate-member' ), count( $update_plugins->response ) ) . ' <a target="_blank" href="' . admin_url( 'update-core.php' ) . '">Update Plugins Now</a>';
foreach ( $update_plugins->response as $plugin_name => $data ) {
$content .= $br . '&nbsp;&nbsp;- ' . $plugin_name;
}
} else {
$content .= $br . $br . $check . __( 'Plugins are up to date.', 'ultimate-member' );
}
if ( isset( $update_themes->response ) && ! empty( $update_themes->response ) ) {
$content .= $br . $br . $flag . sprintf( /* translators: */ _n( 'There\'s %d theme that requires an update.', 'There are %d themes that require updates', count( $update_plugins->response ), 'ultimate-member' ), count( $update_plugins->response ) ) . ' <a target="_blank" href="' . admin_url( 'update-core.php' ) . '">Update Themes Now</a>';
foreach ( $update_themes->response as $theme_name => $data ) {
$content .= $br . '&nbsp;&nbsp;- ' . $theme_name;
}
} else {
$content .= $br . $br . $check . __( 'Themes are up to date.', 'ultimate-member' );
}
if ( isset( $update_themes->current ) && $wp_version !== $update_themes->current ) {
$content .= $br . $br . $flag . __( 'There\'s a new version of WordPress.', 'ultimate-member' ) . '<a target="_blank" href="' . admin_url( 'update-core.php' ) . '">Update WordPress Now</a>';
} else {
$content .= $br . $br . $check . __( 'You\'re using the latest version of WordPress', 'ultimate-member' ) . '(' . esc_attr( $wp_version ) . ')';
}
$content .= $br . $br . __( 'That\'s all. If you have any recommendation on how to secure your site or have questions, please contact us on our <a href="https://ultimatemember.com/feedback/" target="_blank">feedback page</a>. ', 'ultimate-member' );
update_option( 'um_secure_scan_result_content', $content );
return $content;
}
}
+11 -2
View File
@@ -495,10 +495,18 @@ if ( ! class_exists( 'um\Config' ) ) {
'body' => '{display_name} has just deleted their {site_name} account.',
'description' => __('Whether to receive notification when an account is deleted','ultimate-member'),
'recipient' => 'admin'
)
),
'suspicious-activity' => array(
'key' => 'suspicious-activity',
'title' => __( 'Secure: Suspicious Account Activity', 'ultimate-member' ),
'subject' => __( '[{site_name}] Suspicious Account Activity', 'ultimate-member' ),
'body' => 'This is to inform you that there are suspicious activities with the following accounts: {user_profile_link}',
'description' => __( 'Whether to receive notification when suspicious account activity is detected.', 'ultimate-member' ),
'recipient' => 'admin',
'default_active' => true,
),
) );
//settings defaults
$this->settings_defaults = array(
'restricted_access_post_metabox' => array( 'post' => 1, 'page' => 1 ),
@@ -577,6 +585,7 @@ if ( ! class_exists( 'um\Config' ) ) {
'activation_link_expiry_time' => '',
'lock_register_forms' => false,
'display_login_form_notice' => false,
'secure_ban_admins_accounts' => false,
'banned_capabilities' => array( 'manage_options', 'promote_users', 'level_10' ),
'secure_notify_admins_banned_accounts' => false,
'secure_notify_admins_banned_accounts__interval' => 'instant',
+36 -27
View File
@@ -527,7 +527,7 @@ if ( ! class_exists( 'UM' ) ) {
}
//run setup
$this->common()->create_post_types();
$this->common()->cpt()->create_post_types();
$this->setup()->run_setup();
}
@@ -549,10 +549,11 @@ if ( ! class_exists( 'UM' ) ) {
*/
public function includes() {
$this->common();
$this->common()->includes();
$this->access();
if ( $this->is_request( 'ajax' ) ) {
$this->ajax()->includes();
$this->admin();
$this->ajax_init();
$this->admin_ajax_hooks();
@@ -565,6 +566,7 @@ if ( ! class_exists( 'UM' ) ) {
$this->plugin_updater();
$this->theme_updater();
} elseif ( $this->is_request( 'admin' ) ) {
$this->admin()->includes();
$this->admin();
$this->admin_menu();
$this->admin_upgrade();
@@ -572,8 +574,6 @@ if ( ! class_exists( 'UM' ) ) {
$this->columns();
$this->admin_enqueue();
$this->metabox();
$this->admin()->notices();
$this->admin()->secure();
$this->users();
$this->dragdrop();
$this->admin_gdpr();
@@ -581,6 +581,7 @@ if ( ! class_exists( 'UM' ) ) {
$this->plugin_updater();
$this->theme_updater();
} elseif ( $this->is_request( 'frontend' ) ) {
$this->frontend()->includes();
$this->enqueue();
$this->account();
$this->password();
@@ -651,21 +652,6 @@ if ( ! class_exists( 'UM' ) ) {
return $this->classes['blocks'];
}
/**
* @since 2.6.7
*
* @return um\core\Secure()
*/
public function secure() {
if ( empty( $this->classes['secure'] ) ) {
$this->classes['secure'] = new um\core\Secure();
}
return $this->classes['secure'];
}
/**
* Get extension API
*
@@ -704,19 +690,43 @@ if ( ! class_exists( 'UM' ) ) {
return $this->classes[ $key ];
}
/**
* @since 2.6.8
*
* @return um\ajax\Init
*/
public function ajax() {
if ( empty( $this->classes['um\ajax\init'] ) ) {
$this->classes['um\ajax\init'] = new um\ajax\Init();
}
return $this->classes['um\ajax\init'];
}
/**
* @since 2.0
* @since 2.6.8 changed namespace and class content.
*
* @return um\core\Common()
* @return um\common\Init
*/
function common() {
if ( empty( $this->classes['common'] ) ) {
$this->classes['common'] = new um\core\Common();
public function common() {
if ( empty( $this->classes['um\common\init'] ) ) {
$this->classes['um\common\init'] = new um\common\Init();
}
return $this->classes['common'];
return $this->classes['um\common\init'];
}
/**
* @since 2.6.8
*
* @return um\frontend\Init
*/
public function frontend() {
if ( empty( $this->classes['um\frontend\init'] ) ) {
$this->classes['um\frontend\init'] = new um\frontend\Init();
}
return $this->classes['um\frontend\init'];
}
/**
* @since 2.0
@@ -776,7 +786,6 @@ if ( ! class_exists( 'UM' ) ) {
new um\core\AJAX_Common();
}
/**
* @since 2.0.30
*/
@@ -791,9 +800,9 @@ if ( ! class_exists( 'UM' ) ) {
/**
* @since 2.0
*
* @return um\admin\Admin()
* @return um\admin\Admin
*/
function admin() {
public function admin() {
if ( empty( $this->classes['admin'] ) ) {
$this->classes['admin'] = new um\admin\Admin();
}
+92
View File
@@ -0,0 +1,92 @@
<?php
namespace um\common;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\common\CPT' ) ) {
/**
* Class CPT
*
* @package um\common
*
* @since 2.6.8
*/
class CPT {
public function hooks() {
add_action( 'init', array( &$this, 'create_post_types' ), 1 );
}
/**
* Create taxonomies for use for UM
*/
public function create_post_types() {
register_post_type(
'um_form',
array(
'labels' => array(
'name' => __( 'Forms', 'ultimate-member' ),
'singular_name' => __( 'Form', 'ultimate-member' ),
'add_new' => __( 'Add New', 'ultimate-member' ),
'add_new_item' => __( 'Add New Form', 'ultimate-member' ),
'edit_item' => __( 'Edit Form', 'ultimate-member' ),
'not_found' => __( 'You did not create any forms yet', 'ultimate-member' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ultimate-member' ),
'search_items' => __( 'Search Forms', 'ultimate-member' ),
),
'capabilities' => array(
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'edit_others_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'publish_posts' => 'manage_options',
'read_private_posts' => 'manage_options',
),
'show_ui' => true,
'show_in_menu' => false,
'public' => false,
'show_in_rest' => true,
'supports' => array( 'title' ),
)
);
if ( UM()->options()->get( 'members_page' ) ) {
register_post_type(
'um_directory',
array(
'labels' => array(
'name' => __( 'Member Directories', 'ultimate-member' ),
'singular_name' => __( 'Member Directory', 'ultimate-member' ),
'add_new' => __( 'Add New', 'ultimate-member' ),
'add_new_item' => __( 'Add New Member Directory', 'ultimate-member' ),
'edit_item' => __( 'Edit Member Directory', 'ultimate-member' ),
'not_found' => __( 'You did not create any member directories yet', 'ultimate-member' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ultimate-member' ),
'search_items' => __( 'Search Member Directories', 'ultimate-member' ),
),
'capabilities' => array(
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'edit_others_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'publish_posts' => 'manage_options',
'read_private_posts' => 'manage_options',
),
'show_ui' => true,
'show_in_menu' => false,
'public' => false,
'show_in_rest' => true,
'supports' => array( 'title' ),
)
);
}
}
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
namespace um\common;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\common\Init' ) ) {
/**
* Class Init
*
* @package um\common
*/
class Init {
/**
* Create classes' instances where __construct isn't empty for hooks init
*
* @used-by \UM::includes()
*/
public function includes() {
$this->cpt()->hooks();
$this->screen();
$this->secure()->hooks();
}
/**
* @since 2.6.8
*
* @return CPT
*/
public function cpt() {
if ( empty( UM()->classes['um\common\cpt'] ) ) {
UM()->classes['um\common\cpt'] = new CPT();
}
return UM()->classes['um\common\cpt'];
}
/**
* @since 2.6.8
*
* @return Screen
*/
public function screen() {
if ( empty( UM()->classes['um\common\screen'] ) ) {
UM()->classes['um\common\screen'] = new Screen();
}
return UM()->classes['um\common\screen'];
}
/**
* @since 2.6.8
*
* @return Secure
*/
public function secure() {
if ( empty( UM()->classes['um\common\secure'] ) ) {
UM()->classes['um\common\secure'] = new Secure();
}
return UM()->classes['um\common\secure'];
}
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace um\common;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\common\Screen' ) ) {
/**
* Class Screen
*
* @package um\common
*/
class Screen {
/**
* Screen constructor.
*/
public function __construct() {
add_filter( 'body_class', array( &$this, 'remove_admin_bar' ), 1000, 1 );
}
/**
* Remove admin bar classes
*
* @param array $classes
*
* @return array
*/
public function remove_admin_bar( $classes ) {
if ( is_user_logged_in() ) {
if ( um_user( 'can_not_see_adminbar' ) ) {
$search = array_search( 'admin-bar', $classes, true );
if ( ! empty( $search ) ) {
unset( $classes[ $search ] );
}
}
}
return $classes;
}
}
}
+235
View File
@@ -0,0 +1,235 @@
<?php
namespace um\common;
use WP_User;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\common\Secure' ) ) {
/**
* Class Secure
*
* @package um\common
*
* @since 2.6.8
*/
class Secure {
public function hooks() {
add_action( 'wp', array( $this, 'schedule_events' ) );
}
/**
* Add callbacks to Schedule Events.
*
* @since 2.6.8
*/
public function schedule_events() {
if ( ! UM()->options()->get( 'secure_ban_admins_accounts' ) ) {
return;
}
if ( UM()->options()->get( 'secure_notify_admins_banned_accounts' ) ) {
$notification_interval = UM()->options()->get( 'secure_notify_admins_banned_accounts__interval' );
if ( 'instant' === $notification_interval ) {
return;
}
if ( 'hourly' === $notification_interval ) {
add_action( 'um_hourly_scheduled_events', array( $this, 'notify_administrators_hourly' ) );
} elseif ( 'daily' === $notification_interval ) {
add_action( 'um_daily_scheduled_events', array( $this, 'notify_administrators_daily' ) );
}
}
}
/**
* Notify Administrators hourly - Suspicious activities in an hour
*
* @since 2.6.8
*/
public function notify_administrators_hourly() {
$user_ids = get_users(
array(
'fields' => 'ids',
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'um_user_blocked__timestamp',
'value' => gmdate( 'Y-m-d H:i:s', strtotime( '-1 hour' ) ),
'compare' => '>=',
'type' => 'DATETIME',
),
),
)
);
$this->send_notification( $user_ids );
}
/**
* Notify Administrators daily - Today's suspicious activity
*
* @since 2.6.8
*/
public function notify_administrators_daily() {
$user_ids = get_users(
array(
'fields' => 'ids',
'relation' => 'AND',
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'um_user_blocked__timestamp',
'value' => gmdate( 'Y-m-d H:i:s', strtotime( '-1 day' ) ),
'compare' => '>=',
'type' => 'DATE',
),
array(
'key' => 'um_user_blocked__timestamp',
'value' => gmdate( 'Y-m-d H:i:s', strtotime( 'now' ) ),
'compare' => '<=',
'type' => 'DATE',
),
),
)
);
$this->send_notification( $user_ids );
}
public function send_notification( $user_ids ) {
$banned_profile_links = '';
foreach ( $user_ids as $uid ) {
um_fetch_user( $uid );
$banned_profile_links .= UM()->user()->get_profile_link( $uid ) . ' ' . um_user( 'account_status' ) . '<br />';
}
um_reset_user();
$emails = um_multi_admin_email();
if ( ! empty( $emails ) ) {
foreach ( $emails as $email ) {
UM()->mail()->send(
$email,
'suspicious-activity',
array(
'admin' => true,
'tags' => array(
'banned_profile_links',
),
'tags_replace' => array(
$banned_profile_links,
),
)
);
}
}
}
/**
* Get the banned capabilities list.
*
* @return array
*/
public function get_banned_capabilities_list() {
/**
* Filters the banned capabilities for UM Register forms.
*
* @param {array} $capabilities WordPress Administrative Capabilities.
*
* @return {array} Banned admin capabilities.
*
* @since 2.6.8
* @hook um_secure_register_form_banned_capabilities
*
* @example <caption>Added `read` capability as banned.</caption>
* function my_banned_capabilities( $capabilities ) {
* $capabilities[] = 'read';
* return $capabilities;
* }
* add_filter( 'um_secure_register_form_banned_capabilities', 'my_banned_capabilities' );
*/
$banned_admin_capabilities = apply_filters(
'um_secure_register_form_banned_capabilities',
array(
'create_sites',
'delete_sites',
'manage_network',
'manage_sites',
'manage_network_users',
'manage_network_plugins',
'manage_network_themes',
'manage_network_options',
'upgrade_network',
'setup_network',
'activate_plugins',
'edit_dashboard',
'edit_theme_options',
'export',
'import',
'list_users',
'remove_users',
'switch_themes',
'customize',
'delete_site',
'update_core',
'update_plugins',
'update_themes',
'install_plugins',
'install_themes',
'delete_themes',
'delete_plugins',
'edit_plugins',
'edit_themes',
'edit_files',
'edit_users',
'add_users',
'create_users',
'delete_users',
'level_10',
'manage_options',
'promote_users',
)
);
return $banned_admin_capabilities;
}
/**
* Revoke Caps & Mark rejected as suspicious
*
* @param WP_User $user
*
* @since 2.6.8
*/
public function revoke_caps( $user ) {
$user_agent = '';
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
$user_agent = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
}
// Capture details.
$captured = array(
'capabilities' => $user->allcaps,
'submitted' => ! empty( UM()->form()->post_form ) ? UM()->form()->post_form : '',
'roles' => $user->roles,
'user_agent' => $user_agent,
'account_status' => get_user_meta( $user->ID, 'account_status', true ),
);
update_user_meta( $user->ID, 'um_user_blocked__metadata', $captured );
$user->remove_all_caps();
$user->update_user_level_from_caps();
um_fetch_user( $user->ID );
if ( is_user_logged_in() ) {
UM()->user()->set_status( 'inactive' );
} else {
UM()->user()->set_status( 'rejected' );
}
um_reset_user();
update_user_meta( $user->ID, 'um_user_blocked', 'suspicious_activity' );
update_user_meta( $user->ID, 'um_user_blocked__timestamp', current_time( 'mysql' ) );
}
}
}
-118
View File
@@ -1,118 +0,0 @@
<?php
namespace um\core;
if ( ! defined( 'ABSPATH' ) ) exit;
if ( ! class_exists( 'um\core\Common' ) ) {
/**
* Class Common
*
* @package um\core
*/
class Common {
/**
* Common constructor.
*/
function __construct() {
add_action( 'init', array( &$this, 'create_post_types' ), 1 );
add_filter( 'body_class', array( &$this, 'remove_admin_bar' ), 1000, 1 );
}
/**
* Remove admin bar classes
*
* @param array $classes
*
* @return array
*/
function remove_admin_bar( $classes ) {
if ( is_user_logged_in() ) {
if ( um_user( 'can_not_see_adminbar' ) ) {
$search = array_search( 'admin-bar', $classes );
if ( ! empty( $search ) ) {
unset( $classes[ $search ] );
}
}
}
return $classes;
}
/**
* Create taxonomies for use for UM
*/
function create_post_types() {
register_post_type( 'um_form', array(
'labels' => array(
'name' => __( 'Forms', 'ultimate-member' ),
'singular_name' => __( 'Form', 'ultimate-member' ),
'add_new' => __( 'Add New', 'ultimate-member' ),
'add_new_item' => __( 'Add New Form', 'ultimate-member' ),
'edit_item' => __( 'Edit Form', 'ultimate-member' ),
'not_found' => __( 'You did not create any forms yet', 'ultimate-member' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ultimate-member' ),
'search_items' => __( 'Search Forms', 'ultimate-member' ),
),
'capabilities' => array(
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'edit_others_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'publish_posts' => 'manage_options',
'read_private_posts' => 'manage_options',
),
'show_ui' => true,
'show_in_menu' => false,
'public' => false,
'show_in_rest' => true,
'supports' => array( 'title' ),
) );
if ( UM()->options()->get( 'members_page' ) || ! get_option( 'um_options' ) ) {
register_post_type( 'um_directory', array(
'labels' => array(
'name' => __( 'Member Directories', 'ultimate-member' ),
'singular_name' => __( 'Member Directory', 'ultimate-member' ),
'add_new' => __( 'Add New', 'ultimate-member' ),
'add_new_item' => __( 'Add New Member Directory', 'ultimate-member' ),
'edit_item' => __( 'Edit Member Directory', 'ultimate-member' ),
'not_found' => __( 'You did not create any member directories yet', 'ultimate-member' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ultimate-member' ),
'search_items' => __( 'Search Member Directories', 'ultimate-member' ),
),
'capabilities' => array(
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'edit_others_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'publish_posts' => 'manage_options',
'read_private_posts' => 'manage_options',
),
'show_ui' => true,
'show_in_menu' => false,
'public' => false,
'show_in_rest' => true,
'supports' => array( 'title' ),
) );
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace um\frontend;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\frontend\Init' ) ) {
/**
* Class Init
*
* @package um\frontend
*/
class Init {
/**
* Create classes' instances where __construct isn't empty for hooks init
*
* @used-by \UM::includes()
*/
public function includes() {
$this->secure();
}
/**
* @since 2.6.8
*
* @return Secure
*/
public function secure() {
if ( empty( UM()->classes['um\frontend\secure'] ) ) {
UM()->classes['um\frontend\secure'] = new Secure();
}
return UM()->classes['um\frontend\secure'];
}
}
}
@@ -1,27 +1,26 @@
<?php
namespace um\core;
namespace um\frontend;
use WP_Error;
use WP_User;
use WP_User_Query;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\core\Secure' ) ) {
if ( ! class_exists( 'um\frontend\Secure' ) ) {
/**
* Class Secure
*
* @package um\core
* @package um\frontend
*
* @since 2.6.8
*/
class Secure {
/**
* Login constructor.
* Secure constructor.
* @since 2.6.8
*/
public function __construct() {
@@ -36,11 +35,6 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
add_action( 'um_user_login', array( $this, 'login_validate_expired_pass' ), 1 );
add_action( 'validate_password_reset', array( $this, 'avoid_old_password' ), 1, 2 );
/**
* WP Schedule Events for Notification
*/
add_action( 'wp', array( $this, 'schedule_events' ) );
}
/**
@@ -49,6 +43,10 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
* @since 2.6.8
*/
public function init() {
if ( ! UM()->options()->get( 'secure_ban_admins_accounts' ) ) {
return;
}
/**
* Checks the integrity of Current User's Capabilities
*/
@@ -79,7 +77,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
echo "<p class='um-notice warning'>";
echo wp_kses(
sprintf(
// translators: One-time change requires you to reset your password
// translators: One-time change requires you to reset your password
__( '<strong>Important:</strong> Your password has expired. This (one-time) change requires you to reset your password. Please <a href="%s">click here</a> to reset your password via Email.', 'ultimate-member' ),
um_get_core_page( 'password-reset' )
),
@@ -125,10 +123,9 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
/**
* Block all UM Register form submissions.
*
* @param array $args Form settings.
* @since 2.6.8
*/
public function block_register_forms( $args ) {
public function block_register_forms() {
if ( UM()->options()->get( 'lock_register_forms' ) ) {
$login_url = add_query_arg( 'notice', 'maintenance', um_get_core_page( 'login' ) );
nocache_headers();
@@ -186,6 +183,8 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
* Secure user capabilities and revoke administrative ones.
*
* @since 2.6.8
*
* @param int $user_id
*/
public function secure_user_capabilities( $user_id ) {
global $wpdb;
@@ -229,14 +228,14 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
}
if ( $has_admin_cap ) {
$this->revoke_caps( $user );
UM()->common()->secure()->revoke_caps( $user );
/**
* Notify Administrators Immediately
*/
if ( UM()->options()->get( 'secure_notify_admins_banned_accounts' ) ) {
$interval = UM()->options()->get( 'secure_notify_admins_banned_accounts__interval' );
if ( 'instant' === $interval ) {
$this->send_email( array( $user_id ) );
UM()->common()->secure()->send_notification( array( $user_id ) );
}
}
@@ -259,12 +258,13 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
}
/**
* Secure user capabilities and revoke administrative ones.
* Set meta (no need to reset his password) if the user is a new registered.
*
* @since 2.6.8
*
* @param int $user_id
*/
public function maybe_set_whitelisted_password( $user_id ) {
global $wpdb;
$user = get_userdata( $user_id );
if ( empty( $user ) ) {
return;
@@ -275,187 +275,5 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
update_user_meta( $user_id, 'um_secure_has_reset_password__timestamp', current_time( 'mysql' ) );
}
}
/**
* Revoke Caps & Mark rejected as suspicious
*
* @param object $user \WP_User
*
* @since 2.6.8
*/
public function revoke_caps( $user ) {
$user_agent = '';
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
$user_agent = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
}
// Capture details.
$captured = array(
'capabilities' => $user->allcaps,
'submitted' => UM()->form()->post_form,
'roles' => $user->roles,
'user_agent' => $user_agent,
'account_status' => get_user_meta( $user->ID, 'account_status', true ),
);
update_user_meta( $user->ID, 'um_user_blocked__metadata', $captured );
$user->remove_all_caps();
if ( is_user_logged_in() ) {
UM()->user()->set_status( 'inactive' );
} else {
UM()->user()->set_status( 'rejected' );
}
update_user_meta( $user->ID, 'um_user_blocked', 'suspicious_activity' );
update_user_meta( $user->ID, 'um_user_blocked__datetime', current_time( 'mysql' ) );
}
/**
* Add callbacks to Schedule Events.
*
* @since 2.6.8
*/
public function schedule_events() {
if ( UM()->options()->get( 'secure_notify_admins_banned_accounts' ) ) {
$notification_interval = UM()->options()->get( 'secure_notify_admins_banned_accounts__interval' );
if ( 'instant' === $notification_interval ) {
return;
}
if ( 'hourly' === $notification_interval ) {
add_action( 'um_hourly_scheduled_events', array( $this, 'notify_administrators_hourly' ) );
} elseif ( 'daily' === $notification_interval ) {
add_action( 'um_daily_scheduled_events', array( $this, 'notify_administrators_daily' ) );
}
}
}
/**
* Notify Administrators hourly - Suspicious activities in an hour
*
* @since 2.6.8
*/
public function notify_administrators_hourly() {
$args = array(
'fields' => 'ID',
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'um_user_blocked__datetime',
'value' => gmdate( 'Y-m-d H:i:s', strtotime( '-1 hour' ) ),
'compare' => '>=',
'type' => 'DATETIME',
),
),
);
$users = new WP_User_Query( $args );
$this->send_email( array_values( $users->get_results() ) );
}
/**
* Notify Administrators daily - Today's suspicious activity
*
* @since 2.6.8
*/
public function notify_administrators_daily() {
$args = array(
'fields' => 'ID',
'relation' => 'AND',
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'um_user_blocked__datetime',
'value' => gmdate( 'Y-m-d H:i:s', strtotime( '-1 day' ) ),
'compare' => '>=',
'type' => 'DATE',
),
array(
'key' => 'um_user_blocked__datetime',
'value' => gmdate( 'Y-m-d H:i:s', strtotime( 'now' ) ),
'compare' => '<=',
'type' => 'DATE',
),
),
);
$users = new WP_User_Query( $args );
$this->send_email( array_values( $users->get_results() ) );
}
/**
* Get Email template
*
* @param bool $single Whether the template is for single or multiple user activities
* @param array $profile_urls Profile URLs to include in the email body
*
* @since 2.6.8
*/
public function get_email_template( $single = true, $profile_urls = array() ) {
$action = '';
if ( ! is_user_logged_in() ) {
$action = 'Rejected';
} else {
$action = 'Deactivated';
}
$body = '';
if ( $single ) {
$body = 'This is to inform you that there\'s a suspicious activity with the following account: ';
$body .= '<br/>';
$body .= '{user_profile_link}';
$body .= '<br/><br/>';
$body .= 'Due to that we have set the account status to ' . $action . ', Revoked Roles & Destroyed the Login Session.';
$body .= '</br>';
} else {
$body = 'This is to inform you that there are suspicious activities with the following accounts: ';
$body .= '</br>';
$body .= '{user_profile_link}';
$body .= '</br></br>';
$body .= 'Due to that we have set each account\'s status to ' . $action . ', revoked roles & destroyed the login session.';
$body .= '</br>';
}
$urls = implode( '</br>', $profile_urls );
$body = str_replace( '{user_profile_link}', $urls, $body );
$body .= '<br/><br/>- Sent via Ultimate Member plugin. ';
return $body;
}
/**
* Send Email
*
* @param array $user_ids User IDs.
*
* @since 2.6.8
*/
public function send_email( $user_ids = array() ) {
if ( empty( $user_ids ) ) {
return '';
}
$multiple_recipients = array();
$admins = get_users( 'role=Administrator' );
foreach ( $admins as $user ) {
$multiple_recipients[] = $user->user_email;
}
$subject = _n( 'Suspicious Account Activity on ', 'Suspicious Accounts & Activities on ', count( $user_ids ), 'ultimate-member' ) . wp_parse_url( get_site_url() )['host'];
if ( count( $user_ids ) <= 1 ) {
$url = UM()->user()->get_profile_link( $user_ids[0] );
$body = $this->get_email_template( true, array( $url ) );
} else {
$arr_urls = array();
foreach ( $user_ids as $i => $uid ) {
$arr_urls[] = UM()->user()->get_profile_link( $uid );
}
$body = $this->get_email_template( false, $arr_urls );
}
wp_mail( $multiple_recipients, $subject, $body );
}
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
/**
* Template for the "Secure: Suspicious Account Activity".
* Whether to receive notification when suspicious account activity is detected.
*
* This template can be overridden by copying it to {your-theme}/ultimate-member/email/suspicious-activity.php
*
* @version 2.6.8
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<div style="max-width: 560px;padding: 20px;background: #ffffff;border-radius: 5px;margin:40px auto;font-family: Open Sans,Helvetica,Arial;font-size: 15px;color: #666;">
<div style="color: #444444;font-weight: normal;">
<div style="text-align: center;font-weight:600;font-size:26px;padding: 10px 0;border-bottom: solid 3px #eeeeee;">{site_name}</div>
<div style="clear:both"></div>
</div>
<div style="padding: 0 30px 30px 30px;border-bottom: 3px solid #eeeeee;">
<div style="padding: 30px 0;font-size: 14px;">This is to inform you that there are suspicious activities with the following account(s):</div>
<div style="padding: 30px 0;font-size: 14px;">{banned_profile_links}</div>
<div style="padding: 30px 0;font-size: 14px;">Due to that we have set each account(s) status to rejected or deactivated, revoked roles & destroyed the login session.</div>
</div>
<div style="color: #999;padding: 20px 30px">
<div style="">- Sent via Ultimate Member plugin.</div>
</div>
</div>