Merge pull request #1243 from ultimatemember/feature/security-setting-review

Security settings and Scanner
This commit is contained in:
Mykyta Synelnikov
2023-07-10 10:59:53 +03:00
committed by GitHub
22 changed files with 2049 additions and 217 deletions
@@ -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();
});
+27 -3
View File
@@ -88,6 +88,11 @@ if ( ! class_exists( 'um\admin\Admin' ) ) {
add_filter( 'post_updated_messages', array( &$this, 'post_updated_messages' ) );
}
public function includes() {
$this->notices();
$this->secure();
}
function init_variables() {
$this->role_meta = apply_filters(
@@ -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'];
}
}
}
+376
View File
@@ -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 = ' &#183; <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' ) ) {
}
}
}
}
+28 -16
View File
@@ -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>";
}
+123 -15
View File
@@ -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 &amp; 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',
),
)
);
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace um\ajax;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\ajax\Init' ) ) {
/**
* Class Init
*
* @package um\ajax
*/
class Init {
/**
* Create classes' instances where __construct isn't empty for hooks init
*
* @used-by \UM::includes()
*/
public function includes() {
$this->secure();
}
/**
* @since 2.6.8
*
* @return Secure
*/
public function secure() {
if ( empty( UM()->classes['um\ajax\secure'] ) ) {
UM()->classes['um\ajax\secure'] = new Secure();
}
return UM()->classes['um\ajax\secure'];
}
}
}
+451
View File
@@ -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&section=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&section=users' ) . '" target="_blank" >Click here to enable.</a>';
} else {
$content .= $br . $check . 'Your forms are already configured to require of using strong passwords.';
}
$content .= $br . $br . '<strong>Secure Site\'s Connection</strong>';
if ( ! isset( $_SERVER['HTTPS'] ) || 'on' !== $_SERVER['HTTPS'] ) {
$content .= $br . $flag . 'Your site cannot provide a secure connection. Please contact your hosting provider to enable SSL certifications on your server.';
} 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 . '&nbsp;&nbsp;- Register: <a target="_blank" href="' . get_edit_post_link( $fid ) . '">' . get_the_title( $fid ) . '</a> recaptcha ' . ( 1 === $has_captcha ? ' is <strong>enabled</strong> ' . $check : 'is <strong>disabled</strong> ' . $flag );
break;
case 'login':
$has_captcha = absint( get_post_meta( $fid, '_um_login_g_recaptcha_status', true ) );
$content .= $br . '&nbsp;&nbsp;- Login: <a target="_blank" href="' . get_edit_post_link( $fid ) . '">' . get_the_title( $fid ) . '</a> recaptcha ' . ( 1 === $has_captcha ? ' is <strong>enabled</strong> ' . $check : 'is <strong>disabled</strong> ' . $flag );
break;
}
}
$reset_pass_form = absint( UM()->options()->get( 'g_recaptcha_password_reset' ) );
$content .= $br . '&nbsp;&nbsp;- Reset Password Form\'s recaptcha ' . ( 1 === $reset_pass_form ? ' is <strong>enabled</strong> ' . $check : 'is <strong>disabled</strong> ' . $flag );
} elseif ( array_key_exists( 'um-recaptcha/um-recaptcha.php', $all_plugins ) ) {
$content .= $br . $flag . 'Ultimate Member ReCaptcha is installed but not activated.';
} else {
$content .= $br . $flag . 'We recommend that you install and enable <a href="https://wordpress.org/plugins/um-recaptcha/" target="_blank">ReCaptcha</a> to Login & Register forms.';
}
}
$update_plugins = get_site_transient( 'update_plugins' );
$update_themes = get_site_transient( 'update_themes' );
$update_wp_core = get_site_transient( 'update_core' );
global $wp_version;
$content .= $br . $br . '<strong>Keep Themes & Plugins up to date.</strong>';
$content .= $br . __( 'It is important that you update your themes/plugins if the theme/plugin creators update is aimed at fixing security, bug and vulnerability issues. It is not a good idea to ignore available updates as this may give hackers an advantage when trying to access your website.', 'ultimate-member' );
if ( isset( $update_plugins->response ) && ! empty( $update_plugins->response ) ) {
$content .= $br . $br . $flag . sprintf( /* translators: */ _n( 'There\'s %d plugin that requires an update.', 'There are %d plugins that require updates', count( $update_plugins->response ), 'ultimate-member' ), count( $update_plugins->response ) ) . ' <a target="_blank" href="' . admin_url( 'update-core.php' ) . '">Update Plugins Now</a>';
foreach ( $update_plugins->response as $plugin_name => $data ) {
$content .= $br . '&nbsp;&nbsp;- ' . $plugin_name;
}
} else {
$content .= $br . $br . $check . __( 'Plugins are up to date.', 'ultimate-member' );
}
if ( isset( $update_themes->response ) && ! empty( $update_themes->response ) ) {
$content .= $br . $br . $flag . sprintf( /* translators: */ _n( 'There\'s %d theme that requires an update.', 'There are %d themes that require updates', count( $update_plugins->response ), 'ultimate-member' ), count( $update_plugins->response ) ) . ' <a target="_blank" href="' . admin_url( 'update-core.php' ) . '">Update Themes Now</a>';
foreach ( $update_themes->response as $theme_name => $data ) {
$content .= $br . '&nbsp;&nbsp;- ' . $theme_name;
}
} else {
$content .= $br . $br . $check . __( 'Themes are up to date.', 'ultimate-member' );
}
if ( isset( $update_themes->current ) && $wp_version !== $update_themes->current ) {
$content .= $br . $br . $flag . __( 'There\'s a new version of WordPress.', 'ultimate-member' ) . '<a target="_blank" href="' . admin_url( 'update-core.php' ) . '">Update WordPress Now</a>';
} else {
$content .= $br . $br . $check . __( 'You\'re using the latest version of WordPress', 'ultimate-member' ) . '(' . esc_attr( $wp_version ) . ')';
}
$content .= $br . $br . __( 'That\'s all. If you have any recommendation on how to secure your site or have questions, please contact us on our <a href="https://ultimatemember.com/feedback/" target="_blank">feedback page</a>. ', 'ultimate-member' );
update_option( 'um_secure_scan_result_content', $content );
return $content;
}
}
+19 -6
View File
@@ -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' );
+3
View File
@@ -568,6 +568,9 @@ if ( ! class_exists( 'UM_Functions' ) ) {
'charoff' => true,
'valign' => true,
),
'a' => array(
'onclick' => array(),
),
);
break;
case 'templates':
+39 -12
View File
@@ -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();
}
+92
View File
@@ -0,0 +1,92 @@
<?php
namespace um\common;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\common\CPT' ) ) {
/**
* Class CPT
*
* @package um\common
*
* @since 2.6.8
*/
class CPT {
public function hooks() {
add_action( 'init', array( &$this, 'create_post_types' ), 1 );
}
/**
* Create taxonomies for use for UM
*/
public function create_post_types() {
register_post_type(
'um_form',
array(
'labels' => array(
'name' => __( 'Forms', 'ultimate-member' ),
'singular_name' => __( 'Form', 'ultimate-member' ),
'add_new' => __( 'Add New', 'ultimate-member' ),
'add_new_item' => __( 'Add New Form', 'ultimate-member' ),
'edit_item' => __( 'Edit Form', 'ultimate-member' ),
'not_found' => __( 'You did not create any forms yet', 'ultimate-member' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ultimate-member' ),
'search_items' => __( 'Search Forms', 'ultimate-member' ),
),
'capabilities' => array(
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'edit_others_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'publish_posts' => 'manage_options',
'read_private_posts' => 'manage_options',
),
'show_ui' => true,
'show_in_menu' => false,
'public' => false,
'show_in_rest' => true,
'supports' => array( 'title' ),
)
);
if ( UM()->options()->get( 'members_page' ) ) {
register_post_type(
'um_directory',
array(
'labels' => array(
'name' => __( 'Member Directories', 'ultimate-member' ),
'singular_name' => __( 'Member Directory', 'ultimate-member' ),
'add_new' => __( 'Add New', 'ultimate-member' ),
'add_new_item' => __( 'Add New Member Directory', 'ultimate-member' ),
'edit_item' => __( 'Edit Member Directory', 'ultimate-member' ),
'not_found' => __( 'You did not create any member directories yet', 'ultimate-member' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ultimate-member' ),
'search_items' => __( 'Search Member Directories', 'ultimate-member' ),
),
'capabilities' => array(
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'edit_others_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'publish_posts' => 'manage_options',
'read_private_posts' => 'manage_options',
),
'show_ui' => true,
'show_in_menu' => false,
'public' => false,
'show_in_rest' => true,
'supports' => array( 'title' ),
)
);
}
}
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
namespace um\common;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\common\Init' ) ) {
/**
* Class Init
*
* @package um\common
*/
class Init {
/**
* Create classes' instances where __construct isn't empty for hooks init
*
* @used-by \UM::includes()
*/
public function includes() {
$this->cpt()->hooks();
$this->screen();
$this->secure()->hooks();
}
/**
* @since 2.6.8
*
* @return CPT
*/
public function cpt() {
if ( empty( UM()->classes['um\common\cpt'] ) ) {
UM()->classes['um\common\cpt'] = new CPT();
}
return UM()->classes['um\common\cpt'];
}
/**
* @since 2.6.8
*
* @return Screen
*/
public function screen() {
if ( empty( UM()->classes['um\common\screen'] ) ) {
UM()->classes['um\common\screen'] = new Screen();
}
return UM()->classes['um\common\screen'];
}
/**
* @since 2.6.8
*
* @return Secure
*/
public function secure() {
if ( empty( UM()->classes['um\common\secure'] ) ) {
UM()->classes['um\common\secure'] = new Secure();
}
return UM()->classes['um\common\secure'];
}
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace um\common;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\common\Screen' ) ) {
/**
* Class Screen
*
* @package um\common
*/
class Screen {
/**
* Screen constructor.
*/
public function __construct() {
add_filter( 'body_class', array( &$this, 'remove_admin_bar' ), 1000, 1 );
}
/**
* Remove admin bar classes
*
* @param array $classes
*
* @return array
*/
public function remove_admin_bar( $classes ) {
if ( is_user_logged_in() ) {
if ( um_user( 'can_not_see_adminbar' ) ) {
$search = array_search( 'admin-bar', $classes, true );
if ( ! empty( $search ) ) {
unset( $classes[ $search ] );
}
}
}
return $classes;
}
}
}
+257
View File
@@ -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;
}
}
}
-118
View File
@@ -1,118 +0,0 @@
<?php
namespace um\core;
if ( ! defined( 'ABSPATH' ) ) exit;
if ( ! class_exists( 'um\core\Common' ) ) {
/**
* Class Common
*
* @package um\core
*/
class Common {
/**
* Common constructor.
*/
function __construct() {
add_action( 'init', array( &$this, 'create_post_types' ), 1 );
add_filter( 'body_class', array( &$this, 'remove_admin_bar' ), 1000, 1 );
}
/**
* Remove admin bar classes
*
* @param array $classes
*
* @return array
*/
function remove_admin_bar( $classes ) {
if ( is_user_logged_in() ) {
if ( um_user( 'can_not_see_adminbar' ) ) {
$search = array_search( 'admin-bar', $classes );
if ( ! empty( $search ) ) {
unset( $classes[ $search ] );
}
}
}
return $classes;
}
/**
* Create taxonomies for use for UM
*/
function create_post_types() {
register_post_type( 'um_form', array(
'labels' => array(
'name' => __( 'Forms', 'ultimate-member' ),
'singular_name' => __( 'Form', 'ultimate-member' ),
'add_new' => __( 'Add New', 'ultimate-member' ),
'add_new_item' => __( 'Add New Form', 'ultimate-member' ),
'edit_item' => __( 'Edit Form', 'ultimate-member' ),
'not_found' => __( 'You did not create any forms yet', 'ultimate-member' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ultimate-member' ),
'search_items' => __( 'Search Forms', 'ultimate-member' ),
),
'capabilities' => array(
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'edit_others_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'publish_posts' => 'manage_options',
'read_private_posts' => 'manage_options',
),
'show_ui' => true,
'show_in_menu' => false,
'public' => false,
'show_in_rest' => true,
'supports' => array( 'title' ),
) );
if ( UM()->options()->get( 'members_page' ) || ! get_option( 'um_options' ) ) {
register_post_type( 'um_directory', array(
'labels' => array(
'name' => __( 'Member Directories', 'ultimate-member' ),
'singular_name' => __( 'Member Directory', 'ultimate-member' ),
'add_new' => __( 'Add New', 'ultimate-member' ),
'add_new_item' => __( 'Add New Member Directory', 'ultimate-member' ),
'edit_item' => __( 'Edit Member Directory', 'ultimate-member' ),
'not_found' => __( 'You did not create any member directories yet', 'ultimate-member' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ultimate-member' ),
'search_items' => __( 'Search Member Directories', 'ultimate-member' ),
),
'capabilities' => array(
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'edit_others_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'publish_posts' => 'manage_options',
'read_private_posts' => 'manage_options',
),
'show_ui' => true,
'show_in_menu' => false,
'public' => false,
'show_in_rest' => true,
'supports' => array( 'title' ),
) );
}
}
}
}
+48 -44
View File
@@ -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' );
+3 -2
View File
@@ -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 ];
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace um\frontend;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\frontend\Init' ) ) {
/**
* Class Init
*
* @package um\frontend
*/
class Init {
/**
* Create classes' instances where __construct isn't empty for hooks init
*
* @used-by \UM::includes()
*/
public function includes() {
$this->secure();
}
/**
* @since 2.6.8
*
* @return Secure
*/
public function secure() {
if ( empty( UM()->classes['um\frontend\secure'] ) ) {
UM()->classes['um\frontend\secure'] = new Secure();
}
return UM()->classes['um\frontend\secure'];
}
}
}
+269
View File
@@ -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' ) );
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
/**
* Template for the "Secure: Suspicious Account Activity".
* Whether to receive notification when suspicious account activity is detected.
*
* This template can be overridden by copying it to {your-theme}/ultimate-member/email/suspicious-activity.php
*
* @version 2.6.8
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<div style="max-width: 560px;padding: 20px;background: #ffffff;border-radius: 5px;margin:40px auto;font-family: Open Sans,Helvetica,Arial;font-size: 15px;color: #666;">
<div style="color: #444444;font-weight: normal;">
<div style="text-align: center;font-weight:600;font-size:26px;padding: 10px 0;border-bottom: solid 3px #eeeeee;">{site_name}</div>
<div style="clear:both"></div>
</div>
<div style="padding: 0 30px 30px 30px;border-bottom: 3px solid #eeeeee;">
<div style="padding: 30px 0;font-size: 14px;">This is to inform you that there are suspicious activities with the following account(s):</div>
<div style="padding: 30px 0;font-size: 14px;">{banned_profile_links}</div>
<div style="padding: 30px 0;font-size: 14px;">Due to that we have set each account(s) status to rejected or deactivated, revoked roles & destroyed the login session.</div>
</div>
<div style="color: #999;padding: 20px 30px">
<div style="">- Sent via Ultimate Member plugin.</div>
</div>
</div>