mirror of
https://github.com/10h30/ultimatemember.git
synced 2026-07-13 03:36:28 +09:00
Merge pull request #1243 from ultimatemember/feature/security-setting-review
Security settings and Scanner
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
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.after( ' <small style="color: red;">' + wp.i18n.__( 'You can start the scan now but you must save the settings to apply the selected capabilities after the scan is complete.', '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();
|
||||
});
|
||||
@@ -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(
|
||||
@@ -1076,7 +1081,6 @@ if ( ! class_exists( 'um\admin\Admin' ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*
|
||||
@@ -1087,6 +1091,15 @@ if ( ! class_exists( 'um\admin\Admin' ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function sanitize_wp_capabilities_assoc( $value ) {
|
||||
$value = array_map( 'sanitize_key', array_filter( $value ) );
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize role meta fields when wp-admin form has been submitted
|
||||
@@ -2056,17 +2069,28 @@ 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();
|
||||
}
|
||||
return UM()->classes['admin_notices'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.6.8
|
||||
*
|
||||
* @return Secure
|
||||
*/
|
||||
public function secure() {
|
||||
if ( empty( UM()->classes['um\admin\secure'] ) ) {
|
||||
UM()->classes['um\admin\secure'] = new Secure();
|
||||
}
|
||||
return UM()->classes['um\admin\secure'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
<?php
|
||||
/**
|
||||
* 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_Session_Tokens;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'um\admin\Secure' ) ) {
|
||||
|
||||
/**
|
||||
* Class Secure
|
||||
*
|
||||
* @package um\admin
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
class Secure {
|
||||
|
||||
/**
|
||||
* Used for flushing user metas.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $need_flush_meta = false;
|
||||
|
||||
/**
|
||||
* Secure constructor.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_filter( 'um_settings_structure', array( $this, 'add_settings' ) );
|
||||
add_filter( 'manage_users_custom_column', array( $this, 'add_restore_account' ), 9999, 3 );
|
||||
add_filter( 'pre_get_users', array( $this, 'filter_users_by_date_registered' ) );
|
||||
|
||||
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' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter users by Register Date
|
||||
*
|
||||
* @since 2.6.8
|
||||
* @param object $query WP query `pre_get_users`
|
||||
*/
|
||||
public function filter_users_by_date_registered( $query ) {
|
||||
global $pagenow;
|
||||
if ( is_admin() && 'users.php' === $pagenow ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification
|
||||
$date_from = isset( $_GET['um_secure_date_from'] ) ? $_GET['um_secure_date_from'] : null;
|
||||
$date_to = isset( $_GET['um_secure_date_to'] ) ? $_GET['um_secure_date_to'] : null;
|
||||
// phpcs:enable WordPress.Security.NonceVerification
|
||||
if ( ! $date_to ) {
|
||||
$query->set(
|
||||
'date_query',
|
||||
array(
|
||||
'after' => human_time_diff( $date_from, strtotime( current_time( 'mysql' ) ) ) . ' ago',
|
||||
'inclusive' => true,
|
||||
)
|
||||
);
|
||||
} elseif ( $date_from && $date_to ) {
|
||||
$query->set(
|
||||
'date_query',
|
||||
array(
|
||||
'after' => human_time_diff( $date_from, strtotime( current_time( 'mysql' ) ) ) . ' ago',
|
||||
'before' => human_time_diff( $date_to, strtotime( current_time( 'mysql' ) ) ) . ' ago',
|
||||
'inclusive' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle secure actions.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function admin_init() {
|
||||
global $wpdb;
|
||||
// Dismiss admin notice after the first visit to Secure settings page.
|
||||
if ( isset( $_REQUEST['page'] ) && isset( $_REQUEST['tab'] ) &&
|
||||
'um_options' === sanitize_key( $_REQUEST['page'] ) && 'secure' === sanitize_key( $_REQUEST['tab'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
UM()->admin()->notices()->dismiss( 'secure_settings' );
|
||||
}
|
||||
|
||||
if ( isset( $_REQUEST['um_secure_expire_all_sessions'] ) && ! wp_doing_ajax() ) {
|
||||
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'um-secure-expire-session-nonce' ) || ! current_user_can( 'manage_options' ) ) {
|
||||
// This nonce is not valid or current logged-in user has no administrative rights.
|
||||
wp_die( esc_html__( 'Security check', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy all user sessions except the current logged-in user.
|
||||
*/
|
||||
$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->usermeta . ' WHERE meta_key="session_tokens" AND user_id != %d', get_current_user_id() ) );
|
||||
|
||||
if ( UM()->options()->get( 'display_login_form_notice' ) ) {
|
||||
global $wpdb;
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->usermeta} WHERE user_id != %d AND ( meta_key = 'um_secure_has_reset_password' OR meta_key = 'um_secure_has_reset_password__timestamp' )",
|
||||
get_current_user_id()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
wp_safe_redirect( add_query_arg( 'update', 'um_secure_expire_sessions', wp_get_referer() ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( isset( $_REQUEST['um_secure_restore_account'], $_REQUEST['user_id'] ) && ! wp_doing_ajax() ) {
|
||||
$user_id = absint( $_REQUEST['user_id'] );
|
||||
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'um-security-restore-account-nonce-' . $user_id ) || ! current_user_can( 'manage_options' ) ) {
|
||||
// This nonce is not valid or current logged-in user has no administrative rights.
|
||||
wp_die( esc_html__( 'Security check', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$user = get_userdata( $user_id );
|
||||
if ( ! $user ) {
|
||||
wp_die( esc_html__( 'Invalid user.', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
um_fetch_user( $user_id );
|
||||
$metadata = get_user_meta( $user_id, 'um_user_blocked__metadata', true );
|
||||
$user->update_user_level_from_caps();
|
||||
|
||||
// Restore Roles.
|
||||
if ( isset( $metadata['roles'] ) ) {
|
||||
foreach ( $metadata['roles'] as $role ) {
|
||||
$user->add_role( $role );
|
||||
}
|
||||
}
|
||||
// Restore Account Status.
|
||||
if ( isset( $metadata['account_status'] ) ) {
|
||||
UM()->user()->set_status( $metadata['account_status'] );
|
||||
}
|
||||
|
||||
// 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__timestamp' );
|
||||
|
||||
// Don't need to reset a password.
|
||||
if ( UM()->options()->get( 'display_login_form_notice' ) ) {
|
||||
update_user_meta( $user_id, 'um_secure_has_reset_password', true );
|
||||
update_user_meta( $user_id, 'um_secure_has_reset_password__timestamp', current_time( 'mysql' ) );
|
||||
}
|
||||
|
||||
// Clear Cache.
|
||||
UM()->user()->remove_cache( $user_id );
|
||||
um_reset_user();
|
||||
wp_safe_redirect( add_query_arg( 'update', 'um_secure_restore', wp_get_referer() ) );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Secure Settings
|
||||
*
|
||||
* @since 2.6.8
|
||||
*
|
||||
* @param array $settings
|
||||
* @return array
|
||||
*/
|
||||
public function add_settings( $settings ) {
|
||||
$nonce = wp_create_nonce( 'um-secure-expire-session-nonce' );
|
||||
$count_users = count_users();
|
||||
|
||||
$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,
|
||||
'assoc' => true,
|
||||
'checkbox_key' => 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',
|
||||
'label' => __( 'Lock All Register Forms', 'ultimate-member' ),
|
||||
'description' => __( 'This prevents all users from registering with Ultimate Member on your site.', 'ultimate-member' ),
|
||||
),
|
||||
array(
|
||||
'id' => 'display_login_form_notice',
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Display Login form notice to reset passwords', 'ultimate-member' ),
|
||||
'description' => __( 'Enforces users to reset their passwords( one-time ) and prevent from entering old password.', 'ultimate-member' ),
|
||||
),
|
||||
);
|
||||
|
||||
$count_users_exclude_me = $count_users['total_users'] - 1;
|
||||
if ( $count_users_exclude_me > 0 ) {
|
||||
$secure_fields[] = array(
|
||||
'id' => 'force_reset_passwords',
|
||||
'type' => 'info_text',
|
||||
'label' => __( 'Expire All Users Sessions', 'ultimate-member' ),
|
||||
// translators: %d is the users count.
|
||||
'value' => '<a class="button um_secure_force_reset_passwords" href="' . admin_url( '?um_secure_expire_all_sessions=1&_wpnonce=' . esc_attr( $nonce ) ) . '" onclick=\'return confirm("' . esc_js( __( 'Are you sure that you want to make all users sessions expired?', 'ultimate-member' ) ) . '");\'>' . esc_html( sprintf( __( 'Logout Users (%d)', 'ultimate-member' ), $count_users_exclude_me ) ) . '</a>',
|
||||
'description' => __( 'This will log out all users on your site and forces them to reset passwords <br/>when <strong>"Display Login form notice to reset passwords" is enabled/checked.</strong>', 'ultimate-member' ),
|
||||
);
|
||||
}
|
||||
|
||||
$secure_fields = array_merge(
|
||||
$secure_fields,
|
||||
array(
|
||||
array(
|
||||
'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 the Account, Profile & Register forms.', 'ultimate-member' ),
|
||||
'conditional' => array( 'secure_ban_admins_accounts', '=', 1 ),
|
||||
),
|
||||
array(
|
||||
'id' => 'secure_notify_admins_banned_accounts__interval',
|
||||
'type' => 'select',
|
||||
'options' => array(
|
||||
'instant' => __( 'Send Immediately', 'ultimate-member' ),
|
||||
'hourly' => __( 'Hourly', 'ultimate-member' ),
|
||||
'daily' => __( 'Daily', 'ultimate-member' ),
|
||||
),
|
||||
'label' => __( 'Notification Schedule', 'ultimate-member' ),
|
||||
'conditional' => array( 'secure_notify_admins_banned_accounts', '=', 1 ),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$settings['secure'] = array(
|
||||
'title' => __( 'Secure', 'ultimate-member' ),
|
||||
'fields' => $secure_fields,
|
||||
);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append blocked status to the `account_status` column rows.
|
||||
*
|
||||
* @param string $val Default column row value.
|
||||
* @param string $column_name Current column name.
|
||||
* @param int $user_id User ID in loop.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function add_restore_account( $val, $column_name, $user_id ) {
|
||||
if ( 'account_status' === $column_name ) {
|
||||
um_fetch_user( $user_id );
|
||||
$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__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 );
|
||||
$action = ' · <a href=" ' . esc_attr( $restore_account_url ) . ' " onclick=\'return confirm("' . esc_js( __( 'Are you sure that you want to restore this account after getting flagged for suspicious activity?', 'ultimate-member' ) ) . '");\'><small>' . esc_html__( 'Restore Account', 'ultimate-member' ) . '</small></a>';
|
||||
if ( ! empty( $datetime ) ) {
|
||||
$val .= '<div><small>' . human_time_diff( strtotime( $datetime ), strtotime( current_time( 'mysql' ) ) ) . ' ' . __( 'ago', 'ultimate-member' ) . '</small>' . $action . '</div>';
|
||||
}
|
||||
}
|
||||
um_reset_user();
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function check_secure_changes() {
|
||||
if ( isset( $_POST['um_options']['display_login_form_notice'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification
|
||||
$current_option_value = UM()->options()->get( 'display_login_form_notice' );
|
||||
if ( empty( $current_option_value ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $_POST['um_options']['display_login_form_notice'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->need_flush_meta = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function on_settings_save() {
|
||||
if ( isset( $_POST['um_options']['display_login_form_notice'] ) && ! empty( $this->need_flush_meta ) ) { //phpcs:ignore WordPress.Security.NonceVerification
|
||||
global $wpdb;
|
||||
$wpdb->query(
|
||||
"DELETE FROM {$wpdb->usermeta} WHERE meta_key = 'um_secure_has_reset_password' OR meta_key = 'um_secure_has_reset_password__timestamp'"
|
||||
);
|
||||
}
|
||||
|
||||
if ( isset( $_POST['um_options']['secure_notify_admins_banned_accounts'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification
|
||||
if ( ! empty( $_POST['um_options']['secure_notify_admins_banned_accounts'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification
|
||||
UM()->options()->update( 'suspicious-activity_on', 1 );
|
||||
} else {
|
||||
UM()->options()->update( 'suspicious-activity_on', 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,4 +54,4 @@ if ( ! class_exists( 'um\admin\core\Admin_Forms_Settings' ) ) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,13 +117,14 @@ if ( ! class_exists( 'um\admin\core\Admin_Forms' ) ) {
|
||||
$data['value'] = esc_attr( $data['value'] );
|
||||
}
|
||||
|
||||
if( in_array( $data['type'], array('info_text') ) ){
|
||||
if ( 'info_text' === $data['type'] ) {
|
||||
$arr_kses = array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'title' => array(),
|
||||
'target' => array(),
|
||||
'class' => array(),
|
||||
'href' => array(),
|
||||
'title' => array(),
|
||||
'target' => array(),
|
||||
'class' => array(),
|
||||
'onclick' => array(),
|
||||
),
|
||||
'button' => array(
|
||||
'class' => array(),
|
||||
@@ -1185,15 +1186,14 @@ if ( ! class_exists( 'um\admin\core\Admin_Forms' ) ) {
|
||||
* @return bool|string
|
||||
*/
|
||||
function render_multi_checkbox( $field_data ) {
|
||||
|
||||
if ( empty( $field_data['id'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = ( ! empty( $this->form_data['prefix_id'] ) ? $this->form_data['prefix_id'] : '' ) . '_' . $field_data['id'];
|
||||
|
||||
$class = ! empty( $field_data['class'] ) ? $field_data['class'] : '';
|
||||
$class .= ! empty( $field_data['size'] ) ? $field_data['size'] : 'um-long-field';
|
||||
$class = ! empty( $field_data['class'] ) ? $field_data['class'] : '';
|
||||
$class .= ! empty( $field_data['size'] ) ? $field_data['size'] : 'um-long-field';
|
||||
$class_attr = ' class="um-forms-field ' . esc_attr( $class ) . '" ';
|
||||
|
||||
$name = $field_data['id'];
|
||||
@@ -1204,19 +1204,27 @@ if ( ! class_exists( 'um\admin\core\Admin_Forms' ) ) {
|
||||
$values = array();
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$i = 0;
|
||||
$html = '';
|
||||
|
||||
$columns = ( ! empty( $field_data['columns'] ) && is_numeric( $field_data['columns'] ) ) ? $field_data['columns'] : 1;
|
||||
while ( $i < $columns ) {
|
||||
$per_page = ceil( count( $field_data['options'] ) / $columns );
|
||||
$section_fields_per_page = array_slice( $field_data['options'], $i*$per_page, $per_page, true );
|
||||
$html .= '<span class="um-form-fields-section" style="width:' . floor( 100 / $columns ) . '% !important;">';
|
||||
$per_page = ceil( count( $field_data['options'] ) / $columns );
|
||||
$section_fields_per_page = array_slice( $field_data['options'], $i * $per_page, $per_page, true );
|
||||
$html .= '<span class="um-form-fields-section" style="width:' . floor( 100 / $columns ) . '% !important;">';
|
||||
|
||||
foreach ( $section_fields_per_page as $k => $title ) {
|
||||
$id_attr = ' id="' . esc_attr( $id . '_' . $k ) . '" ';
|
||||
$id_attr = ' id="' . esc_attr( $id . '_' . $k ) . '" ';
|
||||
$for_attr = ' for="' . esc_attr( $id . '_' . $k ) . '" ';
|
||||
$name_attr = ' name="' . $name . '[' . $k . ']" ';
|
||||
|
||||
if ( ! empty( $field_data['assoc'] ) ) {
|
||||
$name_attr = ' name="' . esc_attr( $name ) . '[]" ';
|
||||
$value_attr = ' value="' . esc_attr( $k ) . '" ';
|
||||
} else {
|
||||
$name_attr = ' name="' . esc_attr( $name ) . '[' . esc_attr( $k ) . ']" ';
|
||||
$value_attr = ' value="1" ';
|
||||
}
|
||||
$disabed_attr = '';
|
||||
|
||||
$data = array(
|
||||
'field_id' => $field_data['id'] . '_' . $k,
|
||||
@@ -1228,14 +1236,18 @@ if ( ! class_exists( 'um\admin\core\Admin_Forms' ) ) {
|
||||
|
||||
$data_attr = '';
|
||||
foreach ( $data as $key => $value ) {
|
||||
if ( $value == 'checkbox_key' ) {
|
||||
if ( 'checkbox_key' === $value ) {
|
||||
$value = $k;
|
||||
}
|
||||
$data_attr .= ' data-' . $key . '="' . esc_attr( $value ) . '" ';
|
||||
}
|
||||
|
||||
if ( isset( $field_data['options_disabled'] ) && in_array( $k, $field_data['options_disabled'], true ) ) {
|
||||
$disabed_attr = 'disabled="disabled"';
|
||||
}
|
||||
|
||||
$html .= "<label $for_attr>
|
||||
<input type=\"checkbox\" " . checked( in_array( $k, $values ), true, false ) . "$id_attr $name_attr $data_attr value=\"1\" $class_attr>
|
||||
<input type=\"checkbox\" " . checked( in_array( $k, $values, true ), true, false ) . "$disabed_attr $id_attr $name_attr $data_attr $value_attr $class_attr>
|
||||
<span>$title</span>
|
||||
</label>";
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
namespace um\admin\core;
|
||||
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
|
||||
|
||||
@@ -33,6 +33,8 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
|
||||
|
||||
add_action( 'wp_ajax_um_dismiss_notice', array( &$this, 'dismiss_notice' ) );
|
||||
add_action( 'admin_init', array( &$this, 'force_dismiss_notice' ) );
|
||||
|
||||
add_action( 'current_screen', array( &$this, 'create_list_for_screen' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +61,8 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
|
||||
|
||||
//$this->future_changed();
|
||||
|
||||
$this->common_secure();
|
||||
|
||||
/**
|
||||
* UM hook
|
||||
*
|
||||
@@ -79,6 +83,12 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
|
||||
do_action( 'um_admin_create_notices' );
|
||||
}
|
||||
|
||||
public function create_list_for_screen() {
|
||||
if ( UM()->admin()->is_um_screen() ) {
|
||||
$this->secure_settings();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
@@ -511,8 +521,14 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
|
||||
|
||||
case 'err_users_updated':
|
||||
$messages[0]['err_content'] = __( 'Super administrators cannot be modified.', 'ultimate-member' );
|
||||
$messages[1]['content'] = __( 'Other users have been updated.', 'ultimate-member' );
|
||||
|
||||
$messages[1]['content'] = __( 'Other users have been updated.', 'ultimate-member' );
|
||||
break;
|
||||
case 'um_secure_expire_sessions':
|
||||
$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' );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! empty( $messages ) ) {
|
||||
@@ -715,7 +731,6 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
|
||||
), 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check Templates Versions notice
|
||||
*/
|
||||
@@ -746,26 +761,119 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* First time installed Secure settings.
|
||||
*/
|
||||
public function secure_settings() {
|
||||
ob_start();
|
||||
?>
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'Important Update', 'ultimate-member' ); ?></strong><br/>
|
||||
<?php esc_html_e( 'Ultimate Member has a new additional feature to secure your Ultimate Member forms to prevent attacks from injecting accounts with administrative roles & capabilities.', 'ultimate-member' ); ?>
|
||||
</p>
|
||||
<p>
|
||||
<a class="button button-primary" href="<?php echo esc_attr( admin_url( 'admin.php?page=um_options&tab=secure&um_dismiss_notice=secure_settings&um_admin_nonce=' . wp_create_nonce( 'um-admin-nonce' ) ) ); ?>"><?php esc_html_e( 'Manage Security Settings', 'ultimate-member' ); ?></a>
|
||||
<a class="button" target="_blank" href="https://docs.ultimatemember.com/article/1869-security-feature"><?php esc_html_e( 'Read the documentation', 'ultimate-member' ); ?></a>
|
||||
</p>
|
||||
<?php
|
||||
$message = ob_get_clean();
|
||||
$this->add_notice(
|
||||
'secure_settings',
|
||||
array(
|
||||
'class' => 'warning',
|
||||
'message' => $message,
|
||||
'dismissible' => true,
|
||||
),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
function dismiss_notice() {
|
||||
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();
|
||||
|
||||
if ( empty( $_POST['key'] ) ) {
|
||||
wp_send_json_error( __( 'Wrong Data', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$hidden_notices = get_option( 'um_hidden_admin_notices', array() );
|
||||
if ( ! is_array( $hidden_notices ) ) {
|
||||
$hidden_notices = array();
|
||||
}
|
||||
|
||||
$hidden_notices[] = sanitize_key( $_POST['key'] );
|
||||
|
||||
update_option( 'um_hidden_admin_notices', $hidden_notices );
|
||||
$this->dismiss( sanitize_key( $_POST['key'] ) );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss notice by key.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dismiss( $key ) {
|
||||
$hidden_notices = get_option( 'um_hidden_admin_notices', array() );
|
||||
if ( ! is_array( $hidden_notices ) ) {
|
||||
$hidden_notices = array();
|
||||
}
|
||||
$hidden_notices[] = $key;
|
||||
update_option( 'um_hidden_admin_notices', $hidden_notices );
|
||||
}
|
||||
|
||||
function force_dismiss_notice() {
|
||||
if ( ! empty( $_REQUEST['um_dismiss_notice'] ) && ! empty( $_REQUEST['um_admin_nonce'] ) ) {
|
||||
|
||||
@@ -947,6 +947,21 @@ if ( ! class_exists( 'um\admin\core\Admin_Settings' ) ) {
|
||||
'uninstall_on_delete' => array(
|
||||
'sanitize' => 'bool',
|
||||
),
|
||||
'lock_register_forms' => array(
|
||||
'sanitize' => 'bool',
|
||||
),
|
||||
'display_login_form_notice' => array(
|
||||
'sanitize' => 'bool',
|
||||
),
|
||||
'banned_capabilities' => array(
|
||||
'sanitize' => array( UM()->admin(), 'sanitize_wp_capabilities_assoc' ),
|
||||
),
|
||||
'secure_notify_admins_banned_accounts' => array(
|
||||
'sanitize' => 'bool',
|
||||
),
|
||||
'secure_notify_admins_banned_accounts__interval' => array(
|
||||
'sanitize' => 'key',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
<?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' => 'REGEXP',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$suspicious_accounts_count = $suspicious_accounts->get_total();
|
||||
$susp_accounts = $suspicious_accounts->get_results();
|
||||
$arr_dates_registered = array();
|
||||
$arr_suspected_accounts = array();
|
||||
|
||||
/**
|
||||
* Disable and Kickout Suspicious accounts.
|
||||
*/
|
||||
if ( $suspicious_accounts_count > 0 ) {
|
||||
if ( ! empty( $susp_accounts ) ) {
|
||||
foreach ( $susp_accounts as $user ) {
|
||||
|
||||
$arr_suspected_accounts[] = $user->ID;
|
||||
$arr_dates_registered[] = $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();
|
||||
}
|
||||
}
|
||||
|
||||
$arr_dates_in_timestamp = array_map( 'strtotime', $arr_dates_registered );
|
||||
|
||||
$oldest_date = min( $arr_dates_in_timestamp );
|
||||
$newest_date = max( $arr_dates_in_timestamp );
|
||||
|
||||
$might_affected_users = new WP_User_Query(
|
||||
array(
|
||||
'number' => -1,
|
||||
'exclude' => $arr_suspected_accounts,
|
||||
'date_query' => array(
|
||||
'after' => gmdate( 'F d, Y', strtotime( '-1 day', $oldest_date ) ),
|
||||
'before' => gmdate( 'F d, Y', strtotime( '+1 day', $newest_date ) ),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Site Health's Total issues to resolve
|
||||
*/
|
||||
$get_issues = get_transient( 'health-check-site-status-result' );
|
||||
|
||||
$issue_counts = array();
|
||||
|
||||
if ( false !== $get_issues ) {
|
||||
$issue_counts = json_decode( $get_issues, true );
|
||||
}
|
||||
|
||||
if ( ! is_array( $issue_counts ) || ! $issue_counts ) {
|
||||
$issue_counts = array(
|
||||
'recommended' => 0,
|
||||
'critical' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
$site_health_issues_total = $issue_counts['recommended'] + $issue_counts['critical'];
|
||||
|
||||
$content .= '<div style="font-size:19px;padding-bottom:10px;width:100%; display:block;border-bottom:1px solid #ccc;" id="um-secure-scanner-complete">' . ( $suspicious_accounts_count > 0 ? $warning : $check ) . __( 'Scan Complete.', 'ultimate-member' ) . '</div>';
|
||||
|
||||
$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 d, Y', $oldest_date );
|
||||
$nd = gmdate( 'F d, 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' );
|
||||
|
||||
if ( $might_affected_users->get_total() > 0 ) {
|
||||
$od = gmdate( 'F d, Y', $oldest_date );
|
||||
$nd = gmdate( 'F d, Y', $newest_date );
|
||||
if ( $od !== $nd ) {
|
||||
$suspicious_accounts_url = admin_url( 'users.php?um_secure_date_from=' . $oldest_date . '&um_secure_date_to=' . $newest_date );
|
||||
} else {
|
||||
$suspicious_accounts_url = admin_url( 'users.php?um_secure_date_from=' . $oldest_date );
|
||||
}
|
||||
}
|
||||
|
||||
$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 .= '4. 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 .= '👇 MORE RECOMMENDATIONS BELOW.';
|
||||
}
|
||||
|
||||
$content .= $br . $br . '<strong>Review & Resolve Issues with Site Health Check tool</strong>';
|
||||
$content .= $br . __( 'Site Health is a tool in WordPress that helps you monitor how your site is doing. It shows critical information about your WordPress configuration and items that require your attention.', 'ultimate-member' );
|
||||
if ( $site_health_issues_total > 0 ) {
|
||||
$content .= $br . $flag . sprintf( /* translators: %d issue in the Site Health status */ _n( 'There\s %d issue in the Site Health status', 'There are %d issues in the Site Health status', $site_health_issues_total ), $site_health_issues_total );
|
||||
$content .= ': <a target="_blank" href="' . admin_url( 'site-health.php' ) . '">Review Site Health Status</a>';
|
||||
} else {
|
||||
$content .= $br . $check . __( 'There are no issues found in the Site Health status', 'ultimate-member' );
|
||||
}
|
||||
|
||||
$content .= $br . $br . '<strong>Default WP Register Form</strong>';
|
||||
if ( get_option( 'users_can_register' ) ) {
|
||||
$content .= $br . $flag . '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;
|
||||
} else {
|
||||
$content .= $br . $check . 'The default WordPress Register form is disabled.' . $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§ion=other' ) . '">Blocked Email Addresses</a> options.';
|
||||
$content .= $br;
|
||||
} else {
|
||||
$content .= $br . $check . 'Blocked Emails option is already set.';
|
||||
$content .= $br;
|
||||
}
|
||||
|
||||
$content .= $br . '<strong>Manage User Roles & Capabilities</strong>';
|
||||
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 .= $br . $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§ion=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.';
|
||||
} else {
|
||||
$content .= $br . $check . 'Your site provides a secure connection with SSL.';
|
||||
}
|
||||
|
||||
$content .= $br . $br . '<strong>Install Challenge-Response plugin to Login & Register Forms</strong>';
|
||||
if ( ! array_key_exists( 'um-recaptcha/um-recaptcha.php', $all_plugins ) ) {
|
||||
$content .= $br . $flag . 'We recommend that you install and enable <a href="https://wordpress.org/plugins/um-recaptcha/" target="_blank">ReCaptcha</a> to your Reset Password, 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 . ' - 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 . ' - 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 . ' - 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 . ' - ' . $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 . ' - ' . $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;
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,6 @@ if ( ! class_exists( 'um\Config' ) ) {
|
||||
'_um_secondary_color',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* UM hook
|
||||
*
|
||||
@@ -496,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 ),
|
||||
@@ -559,9 +566,9 @@ if ( ! class_exists( 'um\Config' ) ) {
|
||||
'form_asterisk' => 0,
|
||||
'profile_title' => '{display_name} | {site_name}',
|
||||
'profile_desc' => '{display_name} is on {site_name}. Join {site_name} to view {display_name}\'s profile',
|
||||
'admin_email' => get_bloginfo('admin_email'),
|
||||
'mail_from' => get_bloginfo('name'),
|
||||
'mail_from_addr' => get_bloginfo('admin_email'),
|
||||
'admin_email' => get_bloginfo( 'admin_email' ),
|
||||
'mail_from' => get_bloginfo( 'name' ),
|
||||
'mail_from_addr' => get_bloginfo( 'admin_email' ),
|
||||
'email_html' => 1,
|
||||
'image_orientation_by_exif' => 0,
|
||||
'image_compression' => 60,
|
||||
@@ -576,6 +583,12 @@ if ( ! class_exists( 'um\Config' ) ) {
|
||||
'profile_show_html_bio' => 0,
|
||||
'profile_noindex' => 0,
|
||||
'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',
|
||||
);
|
||||
|
||||
add_filter( 'um_get_tabs_from_config', '__return_true' );
|
||||
|
||||
@@ -568,6 +568,9 @@ if ( ! class_exists( 'UM_Functions' ) ) {
|
||||
'charoff' => true,
|
||||
'valign' => true,
|
||||
),
|
||||
'a' => array(
|
||||
'onclick' => array(),
|
||||
),
|
||||
);
|
||||
break;
|
||||
case 'templates':
|
||||
|
||||
+39
-12
@@ -527,8 +527,10 @@ if ( ! class_exists( 'UM' ) ) {
|
||||
}
|
||||
|
||||
//run setup
|
||||
$this->common()->create_post_types();
|
||||
$this->common()->cpt()->create_post_types();
|
||||
$this->setup()->run_setup();
|
||||
|
||||
$this->cron()->schedule_events();
|
||||
}
|
||||
|
||||
|
||||
@@ -549,10 +551,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 +568,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,7 +576,6 @@ if ( ! class_exists( 'UM' ) ) {
|
||||
$this->columns();
|
||||
$this->admin_enqueue();
|
||||
$this->metabox();
|
||||
$this->admin()->notices();
|
||||
$this->users();
|
||||
$this->dragdrop();
|
||||
$this->admin_gdpr();
|
||||
@@ -580,6 +583,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();
|
||||
@@ -607,6 +611,7 @@ if ( ! class_exists( 'UM' ) ) {
|
||||
$this->gdpr();
|
||||
$this->member_directory();
|
||||
$this->blocks();
|
||||
$this->secure();
|
||||
|
||||
//if multisite networks active
|
||||
if ( is_multisite() ) {
|
||||
@@ -649,7 +654,6 @@ if ( ! class_exists( 'UM' ) ) {
|
||||
return $this->classes['blocks'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get extension API
|
||||
*
|
||||
@@ -688,19 +692,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
|
||||
@@ -760,7 +788,6 @@ if ( ! class_exists( 'UM' ) ) {
|
||||
new um\core\AJAX_Common();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.0.30
|
||||
*/
|
||||
@@ -775,9 +802,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();
|
||||
}
|
||||
|
||||
@@ -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' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
<?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_filter( 'um_get_option_filter__banned_capabilities', array( $this, 'add_default_capabilities' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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( $_REQUEST['nonce'], $_REQUEST['action'] ) && 'um_secure_scan_affected_users' === $_REQUEST['action'] && wp_verify_nonce( $_REQUEST['nonce'], 'um-admin-nonce' ) && current_user_can( 'manage_options' ) ) {
|
||||
$user_agent = __( 'Ultimate Member Scanner', 'ultimate-member' );
|
||||
} elseif ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
|
||||
$user_agent = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
|
||||
}
|
||||
|
||||
um_fetch_user( $user->ID );
|
||||
|
||||
// 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' => um_user( 'status' ),
|
||||
);
|
||||
update_user_meta( $user->ID, 'um_user_blocked__metadata', $captured );
|
||||
|
||||
$user->remove_all_caps();
|
||||
$user->update_user_level_from_caps();
|
||||
|
||||
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' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Always add default banned capabilities.
|
||||
*
|
||||
* @param mixed $option_value
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function add_default_capabilities( $option_value ) {
|
||||
if ( is_array( $option_value ) ) {
|
||||
$option_value = array_merge( $option_value, UM()->options()->get_default( 'banned_capabilities' ) );
|
||||
}
|
||||
return $option_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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' ),
|
||||
) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +1,126 @@
|
||||
<?php
|
||||
namespace um\core;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'um\core\Cron' ) ) {
|
||||
|
||||
|
||||
/**
|
||||
* Class Cron
|
||||
* @package um\core
|
||||
*/
|
||||
class Cron {
|
||||
|
||||
|
||||
/**
|
||||
* Cron constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
/**
|
||||
* UM hook
|
||||
*
|
||||
* @type filter
|
||||
* @title um_cron_disable
|
||||
* @description Make UM Cron Actions Enabled or Disabled
|
||||
* @input_vars
|
||||
* [{"var":"$cron_disable","type":"bool","desc":"Disable UM Cron?"}]
|
||||
* @change_log
|
||||
* ["Since: 2.0"]
|
||||
* @usage add_filter( 'um_cron_disable', 'function_name', 10, 1 );
|
||||
* @example
|
||||
* <?php
|
||||
* add_filter( 'um_cron_disable', 'my_cron_disable', 10, 1 );
|
||||
* function my_predefined_field( $cron_disable ) {
|
||||
* // your code here
|
||||
* return $cron_disable;
|
||||
* }
|
||||
* ?>
|
||||
*/
|
||||
$um_cron = apply_filters( 'um_cron_disable', false );
|
||||
if ( $um_cron ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'cron_schedules', array( $this, 'add_schedules' ) );
|
||||
add_action( 'wp', array( $this, 'schedule_Events' ) );
|
||||
add_action( 'wp', array( $this, 'schedule_events' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function cron_disabled() {
|
||||
/**
|
||||
* Filters variable for disable Ultimate Member WP Cron actions.
|
||||
*
|
||||
* @since 2.0
|
||||
* @hook um_cron_disable
|
||||
*
|
||||
* @param {bool} $is_disabled Shortcode arguments.
|
||||
*
|
||||
* @return {bool} Do Cron actions are disabled? True for disable.
|
||||
*
|
||||
* @example <caption>Disable all Ultimate Member WP Cron actions.</caption>
|
||||
* add_filter( 'um_cron_disable', '__return_true' );
|
||||
*/
|
||||
return apply_filters( 'um_cron_disable', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds once weekly to the existing schedules.
|
||||
*
|
||||
* @param array $schedules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_schedules( $schedules = array() ) {
|
||||
if ( $this->cron_disabled() ) {
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
// Adds once weekly to the existing schedules.
|
||||
$schedules['weekly'] = array(
|
||||
'interval' => 604800,
|
||||
'display' => __( 'Once Weekly', 'ultimate-member' )
|
||||
'display' => __( 'Once Weekly', 'ultimate-member' ),
|
||||
);
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function schedule_Events() {
|
||||
public function schedule_events() {
|
||||
if ( $this->cron_disabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->weekly_events();
|
||||
$this->daily_events();
|
||||
$this->twicedaily_events();
|
||||
$this->hourly_events();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function weekly_events() {
|
||||
$sunday_start = wp_date( 'w' );
|
||||
$week_start = $sunday_start - absint( get_option( 'start_of_week' ) );
|
||||
$week_start_day = strtotime( '-' . $week_start . ' days' );
|
||||
$time = mktime( 0, 0, 0, wp_date( 'm', $week_start_day ), wp_date( 'd', $week_start_day ), wp_date( 'Y', $week_start_day ) );
|
||||
if ( ! wp_next_scheduled( 'um_weekly_scheduled_events' ) ) {
|
||||
wp_schedule_event( current_time( 'timestamp' ), 'weekly', 'um_weekly_scheduled_events' );
|
||||
wp_schedule_event( $time, 'weekly', 'um_weekly_scheduled_events' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function daily_events() {
|
||||
if ( ! wp_next_scheduled( 'um_daily_scheduled_events' ) ) {
|
||||
wp_schedule_event( current_time( 'timestamp' ), 'daily', 'um_daily_scheduled_events' );
|
||||
$time = mktime( 0, 0, 0, wp_date( 'm' ), wp_date( 'd' ), wp_date( 'Y' ) );
|
||||
wp_schedule_event( $time, 'daily', 'um_daily_scheduled_events' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function twicedaily_events() {
|
||||
if ( ! wp_next_scheduled( 'um_twicedaily_scheduled_events' ) ) {
|
||||
wp_schedule_event( current_time( 'timestamp' ), 'twicedaily', 'um_twicedaily_scheduled_events' );
|
||||
$time = mktime( 0, 0, 0, wp_date( 'm' ), wp_date( 'd' ), wp_date( 'Y' ) );
|
||||
wp_schedule_event( $time, 'twicedaily', 'um_twicedaily_scheduled_events' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function hourly_events() {
|
||||
if ( ! wp_next_scheduled( 'um_hourly_scheduled_events' ) ) {
|
||||
wp_schedule_event( current_time( 'timestamp' ), 'hourly', 'um_hourly_scheduled_events' );
|
||||
$time = mktime( wp_date( 'H' ), 0, 0, wp_date( 'm' ), wp_date( 'd' ), wp_date( 'Y' ) );
|
||||
wp_schedule_event( $time, 'hourly', 'um_hourly_scheduled_events' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Breaks all Ultimate Member registered schedule events.
|
||||
*/
|
||||
public function unschedule_events() {
|
||||
wp_clear_scheduled_hook( 'um_weekly_scheduled_events' );
|
||||
wp_clear_scheduled_hook( 'um_daily_scheduled_events' );
|
||||
|
||||
@@ -114,12 +114,13 @@ if ( ! class_exists( 'um\core\Options' ) ) {
|
||||
* @use UM()->config()
|
||||
*
|
||||
* @param $option_id
|
||||
* @return bool
|
||||
* @return mixed
|
||||
*/
|
||||
function get_default( $option_id ) {
|
||||
$settings_defaults = UM()->config()->settings_defaults;
|
||||
if ( ! isset( $settings_defaults[ $option_id ] ) )
|
||||
if ( ! isset( $settings_defaults[ $option_id ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $settings_defaults[ $option_id ];
|
||||
}
|
||||
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
namespace um\frontend;
|
||||
|
||||
use WP_Error;
|
||||
use WP_User;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'um\frontend\Secure' ) ) {
|
||||
|
||||
/**
|
||||
* Class Secure
|
||||
*
|
||||
* @package um\frontend
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
class Secure {
|
||||
|
||||
/**
|
||||
* Secure constructor.
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
|
||||
add_action( 'um_before_login_fields', array( $this, 'reset_password_notice' ), 1 );
|
||||
|
||||
add_action( 'um_before_login_fields', array( $this, 'under_maintenance_notice' ), 1 );
|
||||
|
||||
add_action( 'um_submit_form_register', array( $this, 'block_register_forms' ) );
|
||||
|
||||
add_action( 'um_user_login', array( $this, 'login_validate_expired_pass' ), 1 );
|
||||
|
||||
add_action( 'validate_password_reset', array( $this, 'avoid_old_password' ), 1, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds handlers on form submissions.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function init() {
|
||||
if ( ! UM()->options()->get( 'secure_ban_admins_accounts' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the integrity of Current User's Capabilities
|
||||
*/
|
||||
add_action( 'um_after_save_registration_details', array( $this, 'secure_user_capabilities' ), 1 );
|
||||
add_action( 'um_after_save_registration_details', array( $this, 'maybe_set_whitelisted_password' ), 2 );
|
||||
if ( is_user_logged_in() && ! current_user_can( 'manage_options' ) ) { // Exclude current Logged-in Administrator from validation checks.
|
||||
add_action( 'um_after_user_updated', array( $this, 'secure_user_capabilities' ), 1 );
|
||||
add_action( 'um_after_user_account_updated', array( $this, 'secure_user_capabilities' ), 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Login notice for Reset Password
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function reset_password_notice() {
|
||||
if ( ! UM()->options()->get( 'display_login_form_notice' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification
|
||||
if ( ! isset( $_REQUEST['notice'] ) || 'expired_password' !== $_REQUEST['notice'] ) {
|
||||
return;
|
||||
}
|
||||
// phpcs:enable WordPress.Security.NonceVerification
|
||||
|
||||
echo "<p class='um-notice warning'>";
|
||||
echo wp_kses(
|
||||
sprintf(
|
||||
// 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' )
|
||||
),
|
||||
array(
|
||||
'strong' => array(),
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
);
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Login notice for Under Maintance
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function under_maintenance_notice() {
|
||||
if ( ! UM()->options()->get( 'lock_register_forms' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification
|
||||
if ( ! isset( $_GET['notice'] ) || 'maintenance' !== $_GET['notice'] ) {
|
||||
return;
|
||||
}
|
||||
// phpcs:enable WordPress.Security.NonceVerification
|
||||
|
||||
echo "<p class='um-notice warning'>";
|
||||
echo wp_kses(
|
||||
__( '<strong>Important:</strong> This site is currently under maintenance. Please log in or check back soon.', 'ultimate-member' ),
|
||||
array(
|
||||
'strong' => array(),
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
),
|
||||
)
|
||||
);
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Block all UM Register form submissions.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
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();
|
||||
wp_safe_redirect( $login_url );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate when user has expired password
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function login_validate_expired_pass() {
|
||||
if ( UM()->options()->get( 'display_login_form_notice' ) ) {
|
||||
$expired_password_reset = get_user_meta( um_user( 'ID' ), 'um_secure_has_reset_password', true );
|
||||
if ( ! $expired_password_reset ) {
|
||||
$login_url = add_query_arg( 'notice', 'expired_password', um_get_core_page( 'login' ) );
|
||||
wp_safe_redirect( $login_url );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent users from using Old Passwords on UM Password Reset form.
|
||||
*
|
||||
* @param WP_Error $errors
|
||||
* @param WP_User|WP_Error $user
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function avoid_old_password( $errors, $user ) {
|
||||
if ( empty( $_POST['_um_password_change'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! UM()->options()->get( 'display_login_form_notice' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( $_REQUEST['user_password'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$new_user_pass = wp_unslash( $_REQUEST['user_password'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
if ( wp_check_password( $new_user_pass, $user->data->user_pass, $user->ID ) ) {
|
||||
UM()->form()->add_error( 'user_password', __( 'Your new password cannot be same as old password.', 'ultimate-member' ) );
|
||||
$errors->add( 'um_block_old_password', __( 'Your new password cannot be same as old password.', 'ultimate-member' ) );
|
||||
} else {
|
||||
update_user_meta( $user->ID, 'um_secure_has_reset_password', true );
|
||||
update_user_meta( $user->ID, 'um_secure_has_reset_password__timestamp', current_time( 'mysql' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Secure user capabilities and revoke administrative ones.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*
|
||||
* @param int $user_id
|
||||
*/
|
||||
public function secure_user_capabilities( $user_id ) {
|
||||
global $wpdb;
|
||||
$user = get_userdata( $user_id );
|
||||
if ( empty( $user ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the WP_User object of our user.
|
||||
um_fetch_user( $user_id );
|
||||
$has_admin_cap = false;
|
||||
$arr_banned_caps = UM()->options()->get( 'banned_capabilities' );
|
||||
|
||||
if ( is_array( $arr_banned_caps ) ) {
|
||||
foreach ( $arr_banned_caps as $cap ) {
|
||||
/**
|
||||
* When there's at least one administrator cap added to the user,
|
||||
* immediately revoke caps and mark as rejected.
|
||||
*/
|
||||
if ( $user->has_cap( $cap ) ) {
|
||||
$has_admin_cap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $has_admin_cap ) {
|
||||
/**
|
||||
* Double-check if *_user_level has been modified with the highest level
|
||||
* when user has no administrative capabilities.
|
||||
*/
|
||||
$user_level = um_user( $wpdb->get_blog_prefix() . 'user_level' );
|
||||
if ( ! empty( $user_level ) && 10 === absint( $user_level ) ) {
|
||||
$has_admin_cap = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $has_admin_cap ) {
|
||||
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 ) {
|
||||
UM()->common()->secure()->send_notification( array( $user_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy Sessions & Redirect.
|
||||
wp_destroy_current_session();
|
||||
wp_logout();
|
||||
session_unset();
|
||||
$redirect = apply_filters( 'um_secure_blocked_user_redirect_immediately', true );
|
||||
if ( $redirect ) {
|
||||
$login_url = add_query_arg( 'err', 'inactive', um_get_core_page( 'login' ) );
|
||||
wp_safe_redirect( $login_url );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ) {
|
||||
$user = get_userdata( $user_id );
|
||||
if ( empty( $user ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( UM()->options()->get( 'display_login_form_notice' ) ) {
|
||||
update_user_meta( $user_id, 'um_secure_has_reset_password', true );
|
||||
update_user_meta( $user_id, 'um_secure_has_reset_password__timestamp', current_time( 'mysql' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user