mirror of
https://github.com/10h30/ultimatemember.git
synced 2026-06-05 15:09:37 +09:00
- review for secure functionality;
This commit is contained in:
@@ -2068,5 +2068,17 @@ if ( ! class_exists( 'um\admin\Admin' ) ) {
|
||||
}
|
||||
return UM()->classes['admin_notices'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.6.8
|
||||
*
|
||||
* @return core\Secure
|
||||
*/
|
||||
public function secure() {
|
||||
if ( empty( UM()->classes['admin_secure'] ) ) {
|
||||
UM()->classes['admin_secure'] = new core\Secure();
|
||||
}
|
||||
return UM()->classes['admin_secure'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'];
|
||||
@@ -1229,7 +1229,7 @@ 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 ) . '" ';
|
||||
@@ -1237,7 +1237,6 @@ if ( ! class_exists( 'um\admin\core\Admin_Forms' ) ) {
|
||||
|
||||
if ( isset( $field_data['options_disabled'] ) && in_array( $k, $field_data['options_disabled'], true ) ) {
|
||||
$disabed_attr = 'disabled="disabled"';
|
||||
$values = array_merge( $values, $field_data['options_disabled'] );
|
||||
}
|
||||
|
||||
$html .= "<label $for_attr>
|
||||
|
||||
@@ -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' ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +81,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 +519,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 expired.', 'ultimate-member' );
|
||||
break;
|
||||
case 'um_secure_restore':
|
||||
$messages[0]['content'] = __( 'Account has been successfully restored.', 'ultimate-member' );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ! empty( $messages ) ) {
|
||||
@@ -715,7 +729,6 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {
|
||||
), 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check Templates Versions notice
|
||||
*/
|
||||
@@ -746,26 +759,60 @@ 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 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' ),
|
||||
),
|
||||
'secure_notify_admins_banned_accounts' => array(
|
||||
'sanitize' => 'bool',
|
||||
),
|
||||
'secure_notify_admins_banned_accounts__interval' => array(
|
||||
'sanitize' => 'key',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
namespace um\admin\core;
|
||||
|
||||
use WP_User;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'um\admin\core\Secure' ) ) {
|
||||
|
||||
/**
|
||||
* Class Secure
|
||||
*
|
||||
* @package um\admin\core
|
||||
*
|
||||
* @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' ), 10, 3 );
|
||||
|
||||
add_action( 'um_settings_before_save', array( $this, 'check_secure_changes' ) );
|
||||
add_action( 'um_settings_save', array( $this, 'on_settings_save' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle secure actions.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function admin_init() {
|
||||
// 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'] ) ) {
|
||||
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' ) );
|
||||
}
|
||||
|
||||
$users = get_users(
|
||||
array(
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
|
||||
$users = array_values( array_diff( $users, array( get_current_user_id() ) ) );
|
||||
|
||||
if ( ! empty( $users ) ) {
|
||||
foreach ( $users as $user_id ) {
|
||||
// Get an instance of WP_User_Meta_Session_Tokens
|
||||
$sessions_manager = \WP_Session_Tokens::get_instance( $user_id );
|
||||
// Remove all the session for instance user.
|
||||
$sessions_manager->destroy_all();
|
||||
|
||||
// Remove all the session data for all users.
|
||||
//$sessions_manager::drop_sessions();
|
||||
}
|
||||
|
||||
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' ) );
|
||||
}
|
||||
|
||||
$metadata = get_user_meta( $user_id, 'um_user_blocked__metadata', true );
|
||||
|
||||
// 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__datetime' );
|
||||
|
||||
// Don't need to reset a password.
|
||||
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 );
|
||||
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();
|
||||
|
||||
/**
|
||||
* UM hook
|
||||
*
|
||||
* @type filter
|
||||
* @title um_secure_register_form_banned_capabilities
|
||||
* @description Modify banned capabilities for Register forms
|
||||
* @input_vars
|
||||
* [{"var":"$capabilities","type":"array","desc":"WordPress Administratrive Capabilities"}]
|
||||
* @change_log
|
||||
* ["Since: 2.6.8"]
|
||||
* @usage
|
||||
* <?php add_filter( 'um_secure_register_form_banned_capabilities', 'function_name', 10, 1 ); ?>
|
||||
* @example
|
||||
* <?php
|
||||
* add_filter( 'um_secure_register_form_banned_capabilities', 'my_banned_capabilities', 10, 1 );
|
||||
* function my_banned_capabilities( $capabiities ) {
|
||||
* // your code here
|
||||
* $capabiities[ ] = 'read'; // rejects all users with `read` capabilitiy.
|
||||
* return $capabiities;
|
||||
* }
|
||||
* ?>
|
||||
*/
|
||||
$banned_admin_capabilities = apply_filters(
|
||||
'um_secure_register_form_banned_capabilities',
|
||||
array(
|
||||
'create_sites',
|
||||
'delete_sites',
|
||||
'manage_network',
|
||||
'manage_sites',
|
||||
'manage_network_users',
|
||||
'manage_network_plugins',
|
||||
'manage_network_themes',
|
||||
'manage_network_options',
|
||||
'upgrade_network',
|
||||
'setup_network',
|
||||
'activate_plugins',
|
||||
'edit_dashboard',
|
||||
'edit_theme_options',
|
||||
'export',
|
||||
'import',
|
||||
'list_users',
|
||||
'remove_users',
|
||||
'switch_themes',
|
||||
'customize',
|
||||
'delete_site',
|
||||
'update_core',
|
||||
'update_plugins',
|
||||
'update_themes',
|
||||
'install_plugins',
|
||||
'install_themes',
|
||||
'delete_themes',
|
||||
'delete_plugins',
|
||||
'edit_plugins',
|
||||
'edit_themes',
|
||||
'edit_files',
|
||||
'edit_users',
|
||||
'add_users',
|
||||
'create_users',
|
||||
'delete_users',
|
||||
'level_10',
|
||||
'manage_options',
|
||||
'promote_users',
|
||||
)
|
||||
);
|
||||
|
||||
$banned_capabilities = array();
|
||||
foreach ( $banned_admin_capabilities as $cap ) {
|
||||
$banned_capabilities[ $cap ] = $cap;
|
||||
}
|
||||
|
||||
$secure_fields = array(
|
||||
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' ),
|
||||
);
|
||||
}
|
||||
|
||||
$disabled_capabilities = UM()->options()->get_default( 'banned_capabilities' );
|
||||
$disabled_capabilities_text = '<strong>' . implode( '</strong>, <strong>', $disabled_capabilities ) . '</strong>';
|
||||
|
||||
$secure_fields = array_merge(
|
||||
$secure_fields,
|
||||
array(
|
||||
array(
|
||||
'id' => 'banned_capabilities',
|
||||
'type' => 'multi_checkbox',
|
||||
'multi' => true,
|
||||
'columns' => 2,
|
||||
'options_disabled' => UM()->options()->get_default( 'banned_capabilities' ),
|
||||
'options' => $banned_capabilities,
|
||||
'label' => __( 'Banned Administrative Capabilities', 'ultimate-member' ),
|
||||
// translators: %s are disabled default capabilities that are enabled by default.
|
||||
'description' => sprintf( __( 'All the above are default Administrator & Super Admin capabilities. When someone tries to inject capabilities to the Account, Profile & Register forms submission, it will be flagged with this option. The %s capabilities are locked to ensure no users will be created with these capabilities.', 'ultimate-member' ), $disabled_capabilities_text ),
|
||||
),
|
||||
array(
|
||||
'id' => 'secure_notify_admins_banned_accounts',
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Notify Administrators', 'ultimate-member' ),
|
||||
'description' => __( 'When enabled, All administrators will be notified when someone has suspicious activities in Profile & Register forms.', 'ultimate-member' ),
|
||||
),
|
||||
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__datetime' );
|
||||
$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'] ) ) {
|
||||
$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'] ) ) {
|
||||
$this->need_flush_meta = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function on_settings_save() {
|
||||
if ( isset( $_POST['um_options']['display_login_form_notice'] ) && ! empty( $this->need_flush_meta ) ) {
|
||||
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'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +195,6 @@ if ( ! class_exists( 'um\Config' ) ) {
|
||||
'_um_secondary_color',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* UM hook
|
||||
*
|
||||
@@ -559,9 +558,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 +575,11 @@ 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,
|
||||
'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':
|
||||
|
||||
@@ -573,6 +573,7 @@ if ( ! class_exists( 'UM' ) ) {
|
||||
$this->admin_enqueue();
|
||||
$this->metabox();
|
||||
$this->admin()->notices();
|
||||
$this->admin()->secure();
|
||||
$this->users();
|
||||
$this->dragdrop();
|
||||
$this->admin_gdpr();
|
||||
|
||||
@@ -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 ];
|
||||
}
|
||||
|
||||
+120
-411
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace um\core;
|
||||
|
||||
use WP_Error;
|
||||
use WP_User;
|
||||
use WP_User_Query;
|
||||
|
||||
@@ -19,27 +20,16 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
*/
|
||||
class Secure {
|
||||
|
||||
/**
|
||||
* Banned Administrative Capabilities
|
||||
*
|
||||
* @since 2.6.8
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $banned_admin_capabilities = array();
|
||||
|
||||
/**
|
||||
* Login constructor.
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
|
||||
add_action( 'um_before_login_fields', array( $this, 'reset_password_notice' ) );
|
||||
add_action( 'um_before_login_fields', array( $this, 'reset_password_notice' ), 1 );
|
||||
|
||||
add_action( 'um_before_login_fields', array( $this, 'under_maintanance_notice' ) );
|
||||
|
||||
add_filter( 'um_settings_structure', array( $this, 'add_settings' ) );
|
||||
add_action( 'um_before_login_fields', array( $this, 'under_maintenance_notice' ), 1 );
|
||||
|
||||
add_action( 'um_submit_form_register', array( $this, 'block_register_forms' ) );
|
||||
|
||||
@@ -47,176 +37,35 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
|
||||
add_action( 'validate_password_reset', array( $this, 'avoid_old_password' ), 1, 2 );
|
||||
|
||||
/**
|
||||
* UM hook
|
||||
*
|
||||
* @type filter
|
||||
* @title um_secure_register_form_banned_capabilities
|
||||
* @description Modify banned capabilities for Register forms
|
||||
* @input_vars
|
||||
* [{"var":"$capabilities","type":"array","desc":"WordPress Administratrive Capabilities"}]
|
||||
* @change_log
|
||||
* ["Since: 2.6.8"]
|
||||
* @usage
|
||||
* <?php add_filter( 'um_secure_register_form_banned_capabilities', 'function_name', 10, 1 ); ?>
|
||||
* @example
|
||||
* <?php
|
||||
* add_filter( 'um_secure_register_form_banned_capabilities', 'my_banned_capabilities', 10, 1 );
|
||||
* function my_banned_capabilities( $capabiities ) {
|
||||
* // your code here
|
||||
* $capabiities[ ] = 'read'; // rejects all users with `read` capabilitiy.
|
||||
* return $capabiities;
|
||||
* }
|
||||
* ?>
|
||||
*/
|
||||
$this->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',
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Add blocked status in the User column list.
|
||||
*/
|
||||
add_filter( 'manage_users_custom_column', array( $this, 'manage_users_custom_column' ), 10, 3 );
|
||||
|
||||
/**
|
||||
* WP Schedule Events for Notification
|
||||
*/
|
||||
add_action( 'wp', array( $this, 'schedule_events' ) );
|
||||
|
||||
/**
|
||||
* Init
|
||||
*/
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
|
||||
/**
|
||||
* Admin Notice
|
||||
*/
|
||||
add_action( 'admin_notices', array( $this, 'admin_notice' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Init
|
||||
* Adds handlers on form submissions.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
/**
|
||||
* Checks the integrity of Current User's Capabilities
|
||||
*/
|
||||
add_action( 'um_after_save_registration_details', array( $this, 'secure_user_capabilities' ), 10 );
|
||||
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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin Init
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function admin_init() {
|
||||
|
||||
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 administrativerights.
|
||||
wp_die( esc_html_e( 'Security check', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
// Get an instance of WP_User_Meta_Session_Tokens
|
||||
$sessions_manager = \WP_Session_Tokens::get_instance( null );
|
||||
// Remove all the session data for all users.
|
||||
$sessions_manager->drop_sessions();
|
||||
wp_safe_redirect( admin_url() );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( isset( $_REQUEST['um_secure_restore_account'] ) && isset( $_REQUEST['user_id'] ) && ( ! wp_doing_ajax() ) ) {
|
||||
$user_id = $_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 administrativerights.
|
||||
wp_die( esc_html_e( 'Security check', 'ultimate-member' ) );
|
||||
}
|
||||
|
||||
$user = new WP_User( $user_id );
|
||||
$metadata = $user->get( 'um_user_blocked__metadata' );
|
||||
|
||||
// Restore Roles.
|
||||
if ( isset( $metadata['roles'] ) ) {
|
||||
foreach ( $metadata['roles'] as $role ) {
|
||||
$user->add_role( $role );
|
||||
}
|
||||
}
|
||||
// Restore Account Status.
|
||||
if ( isset( $metadata['account_status'] ) ) {
|
||||
update_user_meta( $user_id, 'account_status', $metadata['account_status'] );
|
||||
}
|
||||
// Clear Cache
|
||||
UM()->user()->remove_cache( $user_id );
|
||||
|
||||
set_transient( 'um_secure_restore_account_notice_success', 1, 5 );
|
||||
|
||||
wp_safe_redirect( wp_get_referer() );
|
||||
exit;
|
||||
|
||||
}
|
||||
|
||||
if ( isset( $_REQUEST['um_dismiss_security_first_time_notice'] ) ) {
|
||||
set_transient( 'um_secure_first_time_admin_notice', 1, 0 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Login notice for Reset Password
|
||||
*
|
||||
* @param array $args
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function reset_password_notice( $args ) {
|
||||
|
||||
public function reset_password_notice() {
|
||||
if ( ! UM()->options()->get( 'display_login_form_notice' ) ) {
|
||||
return;
|
||||
}
|
||||
@@ -247,28 +96,22 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
/**
|
||||
* Add Login notice for Under Maintance
|
||||
*
|
||||
* @param array $args
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function under_maintanance_notice( $args ) {
|
||||
|
||||
public function under_maintenance_notice() {
|
||||
if ( ! UM()->options()->get( 'lock_register_forms' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification
|
||||
if ( ! isset( $_REQUEST['notice'] ) || 'maintanance' !== $_REQUEST['notice'] ) {
|
||||
if ( ! isset( $_GET['notice'] ) || 'maintenance' !== $_GET['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> This site is currently under maintenance. Please check back soon.', 'ultimate-member' ),
|
||||
um_get_core_page( 'password-reset' )
|
||||
),
|
||||
__( '<strong>Important:</strong> This site is currently under maintenance. Please log in or check back soon.', 'ultimate-member' ),
|
||||
array(
|
||||
'strong' => array(),
|
||||
'a' => array(
|
||||
@@ -279,83 +122,6 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Secure Settings
|
||||
*
|
||||
* @param array $settings
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function add_settings( $settings ) {
|
||||
$nonce = wp_create_nonce( 'um-secure-expire-session-nonce' );
|
||||
$count_users = count_users();
|
||||
$settings['secure']['title'] = __( 'Secure', 'ultimate-member' );
|
||||
|
||||
$banned_admin_capabilities_options = array();
|
||||
$default_locked_cap_options = array( 'manage_options', 'promote_users', 'level_10' );
|
||||
foreach ( $this->banned_admin_capabilities as $i => $cap ) {
|
||||
$banned_admin_capabilities_options[ $cap ] = $cap;
|
||||
}
|
||||
|
||||
$settings['secure']['sections'] =
|
||||
array(
|
||||
'' =>
|
||||
array(
|
||||
'title' => __( 'Secure Ultimate Member', 'ultimate-member' ),
|
||||
'fields' => array(
|
||||
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' ),
|
||||
),
|
||||
array(
|
||||
'id' => 'force_reset_passwords',
|
||||
'type' => 'info_text',
|
||||
'label' => __( 'Expire All Users Sessions', 'ultimate-member' ),
|
||||
'value' => '<a href="' . admin_url( '?um_secure_expire_all_sessions=1&_wpnonce=' . esc_attr( $nonce ) ) . '" class="button">Logout Users(' . esc_attr( $count_users['total_users'] ) . ') </a>',
|
||||
'description' => __( 'This will logout 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' ),
|
||||
),
|
||||
array(
|
||||
'id' => 'banned_capabilities',
|
||||
'type' => 'multi_checkbox',
|
||||
'multi' => true,
|
||||
'columns' => 2,
|
||||
'options_disabled' => $default_locked_cap_options,
|
||||
'options' => $banned_admin_capabilities_options,
|
||||
'value' => UM()->options()->get( 'banned_capabilities' ) ? array_keys( UM()->options()->get( 'banned_capabilities' ) ) : array_keys( $default_locked_cap_options ),
|
||||
'label' => __( 'Banned Administrative Capabilities', 'ultimate-member' ),
|
||||
'description' => __( '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 <strong>manage_options</strong>, <strong>promote_users</strong> & <strong>level_10</strong> capabilities are locked to ensure no users will be created with these capabilities.', 'ultimate-member' ),
|
||||
),
|
||||
array(
|
||||
'id' => 'secure_notify_admins_banned_accounts',
|
||||
'type' => 'checkbox',
|
||||
'label' => __( 'Notify Administrators', 'ultimate-member' ),
|
||||
'description' => __( 'When enabled, All administrators will be notified when someone has suspicious activities in Profile & Register forms.', 'ultimate-member' ),
|
||||
),
|
||||
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 ),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block all UM Register form submissions.
|
||||
*
|
||||
@@ -364,7 +130,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
*/
|
||||
public function block_register_forms( $args ) {
|
||||
if ( UM()->options()->get( 'lock_register_forms' ) ) {
|
||||
$login_url = add_query_arg( 'notice', 'maintanance', um_get_core_page( 'login' ) );
|
||||
$login_url = add_query_arg( 'notice', 'maintenance', um_get_core_page( 'login' ) );
|
||||
nocache_headers();
|
||||
wp_safe_redirect( $login_url );
|
||||
exit;
|
||||
@@ -374,14 +140,12 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
/**
|
||||
* Validate when user has expired password
|
||||
*
|
||||
* @param array $submitted_data
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function login_validate_expired_pass( $submitted_data ) {
|
||||
|
||||
public function login_validate_expired_pass() {
|
||||
if ( UM()->options()->get( 'display_login_form_notice' ) ) {
|
||||
$has_expired = get_user_meta( um_user( 'ID' ), 'um_secure_has_reset_password', true );
|
||||
if ( ! $has_expired ) {
|
||||
$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;
|
||||
@@ -390,36 +154,48 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent users from using Old Passwords on Password Reset form
|
||||
* Prevent users from using Old Passwords on UM Password Reset form.
|
||||
*
|
||||
* @param WP_Error $errors
|
||||
* @param WP_User|WP_Error $user
|
||||
*
|
||||
* @param object $errors
|
||||
* @param object $user
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function avoid_old_password( $errors, $user ) {
|
||||
$wp_hasher = new \PasswordHash( 8, true );
|
||||
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 = $_REQUEST['user_password']; // phpcs:ignore WordPress.Security.NonceVerification
|
||||
if ( $wp_hasher->CheckPassword( $new_user_pass, $user->data->user_pass ) ) {
|
||||
$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( 'block_old_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->data->ID, 'um_secure_has_reset_password', true );
|
||||
update_user_meta( $user->data->ID, 'um_secure_has_reset_password__timestamp', current_time( 'mysql' ) );
|
||||
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
|
||||
* Secure user capabilities and revoke administrative ones.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
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 );
|
||||
$user = new WP_User( $user_id );
|
||||
$has_admin_cap = false;
|
||||
$arr_banned_caps = array();
|
||||
|
||||
@@ -427,43 +203,40 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
$arr_banned_caps = array_keys( UM()->options()->get( 'banned_capabilities' ) );
|
||||
}
|
||||
|
||||
// Add locked administratrive capabilities
|
||||
$arr_banned_caps[] = 'manage_options';
|
||||
$arr_banned_caps[] = 'promote_users';
|
||||
$arr_banned_caps[] = 'level_10';
|
||||
// Add locked administrative capabilities.
|
||||
$arr_banned_caps = array_merge( $arr_banned_caps, UM()->options()->get_default( 'banned_capabilities' ) );
|
||||
|
||||
foreach ( $arr_banned_caps as $i => $cap ) {
|
||||
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;
|
||||
$this->revoke_caps( $user );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ) ) {
|
||||
if ( 10 === $user_level ) {
|
||||
$this->revoke_caps( $user );
|
||||
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 ) {
|
||||
$this->revoke_caps( $user );
|
||||
/**
|
||||
* Notify Administrators Immediately
|
||||
*/
|
||||
if ( UM()->options()->get( 'secure_notify_admins_banned_accounts' ) ) {
|
||||
$interval = UM()->options()->get( 'secure_notify_admins_banned_accounts__interval' );
|
||||
if ( 'instant' === $interval ) {
|
||||
$this->send_email( array( $user->get( 'ID' ) ) );
|
||||
$this->send_email( array( $user_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,39 +250,53 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
wp_safe_redirect( $login_url );
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
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' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
/**
|
||||
* Secure user capabilities and revoke administrative ones.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function maybe_set_whitelisted_password( $user_id ) {
|
||||
global $wpdb;
|
||||
$user = get_userdata( $user_id );
|
||||
if ( empty( $user ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
return false;
|
||||
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' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke Caps & Mark rejected as suspicious
|
||||
*
|
||||
* @param string $cap Capability slug
|
||||
* @param object $user \WP_User
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function revoke_caps( $user ) {
|
||||
|
||||
if ( ! class_exists( '\Browser' ) ) {
|
||||
require_once um_path . 'includes/lib/browser.php';
|
||||
$user_agent = '';
|
||||
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
|
||||
$user_agent = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
|
||||
}
|
||||
|
||||
// Detect browser.
|
||||
$browser = new \Browser();
|
||||
// Capture details.
|
||||
$captured = array(
|
||||
'capabilities' => $user->allcaps,
|
||||
'submitted' => UM()->form()->post_form,
|
||||
'roles' => $user->roles,
|
||||
'user_browser' => $browser,
|
||||
'account_status' => get_user_meta( $user->get( 'ID' ), 'account_status', true ),
|
||||
'user_agent' => $user_agent,
|
||||
'account_status' => get_user_meta( $user->ID, 'account_status', true ),
|
||||
);
|
||||
update_user_meta( $user->get( 'ID' ), 'um_user_blocked__metadata', $captured );
|
||||
update_user_meta( $user->ID, 'um_user_blocked__metadata', $captured );
|
||||
|
||||
$user->remove_all_caps();
|
||||
if ( is_user_logged_in() ) {
|
||||
@@ -517,58 +304,26 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
} else {
|
||||
UM()->user()->set_status( 'rejected' );
|
||||
}
|
||||
update_user_meta( $user->get( 'ID' ), 'um_user_blocked', 'suspicious_activity' );
|
||||
update_user_meta( $user->get( 'ID' ), 'um_user_blocked__datetime', current_time( 'mysql' ) );
|
||||
update_user_meta( $user->ID, 'um_user_blocked', 'suspicious_activity' );
|
||||
update_user_meta( $user->ID, 'um_user_blocked__datetime', current_time( 'mysql' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Append blocked status to the `account_status` column rows
|
||||
*
|
||||
* @param string $val Default column row value
|
||||
* @param string $column_name Current column name
|
||||
* @param integer $user_id User ID in loop
|
||||
*
|
||||
* @since 2.6.8
|
||||
*
|
||||
* @return string $val
|
||||
*/
|
||||
public function manage_users_custom_column( $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__datetime' );
|
||||
$val = $val . '<div><small>' . __( '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("' . __( 'Are you sure that you want to restore this account after getting flagged for suspicious activity?', 'utimate-member' ) . '");\'><small>' . __( 'Restore Account', 'ultimate-member' ) . '</small></a>';
|
||||
if ( ! empty( $datetime ) ) {
|
||||
$val = $val . '<div><small>' . human_time_diff( strtotime( $datetime ), strtotime( current_time( 'mysql' ) ) ) . ' ' . __( 'ago', 'ultimate-member' ) . '</small>' . $action . '</div>';
|
||||
}
|
||||
}
|
||||
um_reset_user();
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Events
|
||||
* Add callbacks to Schedule Events.
|
||||
*
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function schedule_events() {
|
||||
|
||||
if ( UM()->options()->get( 'secure_notify_admins_banned_accounts' ) ) {
|
||||
add_action( 'um_secure_notify_administrator_hourly', array( $this, 'notify_administrators_hourly' ) );
|
||||
add_action( 'um_secure_notify_administrator_daily', array( $this, 'notify_administrators_daily' ) );
|
||||
|
||||
if ( ! wp_next_scheduled( 'um_secure_notify_administrator_hourly' ) ) {
|
||||
wp_schedule_event( current_time( 'mysql' ), 'hourly', 'um_secure_notify_administrator_hourly' );
|
||||
$notification_interval = UM()->options()->get( 'secure_notify_admins_banned_accounts__interval' );
|
||||
if ( 'instant' === $notification_interval ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! wp_next_scheduled( 'um_secure_notify_administrator_daily' ) ) {
|
||||
wp_schedule_event( current_time( 'mysql' ), 'daily', 'um_secure_notify_administrator_daily' );
|
||||
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' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,27 +334,22 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function notify_administrators_hourly() {
|
||||
|
||||
$interval = UM()->options()->get( 'secure_notify_admins_banned_accounts__interval' );
|
||||
if ( 'hourly' === $interval ) {
|
||||
$args = array(
|
||||
'fields' => 'ID',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'um_user_blocked__datetime',
|
||||
'value' => gmdate( 'Y-m-d H:i:s', strtotime( '-1 hour' ) ),
|
||||
'compare' => '>=',
|
||||
'type' => 'DATETIME',
|
||||
),
|
||||
$args = array(
|
||||
'fields' => 'ID',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'um_user_blocked__datetime',
|
||||
'value' => gmdate( 'Y-m-d H:i:s', strtotime( '-1 hour' ) ),
|
||||
'compare' => '>=',
|
||||
'type' => 'DATETIME',
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
|
||||
$users = new WP_User_Query( $args );
|
||||
|
||||
$this->send_email( array_values( $users->get_results() ) );
|
||||
}
|
||||
$users = new WP_User_Query( $args );
|
||||
|
||||
$this->send_email( array_values( $users->get_results() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -608,35 +358,29 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
* @since 2.6.8
|
||||
*/
|
||||
public function notify_administrators_daily() {
|
||||
|
||||
$interval = UM()->options()->get( 'secure_notify_admins_banned_accounts__interval' );
|
||||
if ( 'daily' === $interval ) {
|
||||
$args = array(
|
||||
'fields' => 'ID',
|
||||
'relation' => 'AND',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'um_user_blocked__datetime',
|
||||
'value' => gmdate( 'Y-m-d H:i:s', strtotime( '-1 day' ) ),
|
||||
'compare' => '>=',
|
||||
'type' => 'DATE',
|
||||
),
|
||||
array(
|
||||
'key' => 'um_user_blocked__datetime',
|
||||
'value' => gmdate( 'Y-m-d H:i:s', strtotime( 'now' ) ),
|
||||
'compare' => '<=',
|
||||
'type' => 'DATE',
|
||||
),
|
||||
$args = array(
|
||||
'fields' => 'ID',
|
||||
'relation' => 'AND',
|
||||
'meta_query' => array(
|
||||
'relation' => 'AND',
|
||||
array(
|
||||
'key' => 'um_user_blocked__datetime',
|
||||
'value' => gmdate( 'Y-m-d H:i:s', strtotime( '-1 day' ) ),
|
||||
'compare' => '>=',
|
||||
'type' => 'DATE',
|
||||
),
|
||||
);
|
||||
array(
|
||||
'key' => 'um_user_blocked__datetime',
|
||||
'value' => gmdate( 'Y-m-d H:i:s', strtotime( 'now' ) ),
|
||||
'compare' => '<=',
|
||||
'type' => 'DATE',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$users = new WP_User_Query( $args );
|
||||
|
||||
$this->send_email( array_values( $users->get_results() ) );
|
||||
|
||||
}
|
||||
$users = new WP_User_Query( $args );
|
||||
|
||||
$this->send_email( array_values( $users->get_results() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,40 +457,5 @@ if ( ! class_exists( 'um\core\Secure' ) ) {
|
||||
wp_mail( $multiple_recipients, $subject, $body );
|
||||
|
||||
}
|
||||
|
||||
public function admin_notice() {
|
||||
if ( get_transient( 'um_secure_restore_account_notice_success' ) ) { ?>
|
||||
<div class="updated notice is-dismissible">
|
||||
<p>
|
||||
<?php
|
||||
esc_html_e( 'Account has been succesfully restored.', 'um-stripe' );
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
// Delete transient, only display this notice once.
|
||||
delete_transient( 'um_secure_restore_account_notice_success' );
|
||||
}
|
||||
// phpcs:disable WordPress.Security.NonceVerification
|
||||
if ( ! get_transient( 'um_secure_first_time_admin_notice' ) && ( ! isset( $_REQUEST['page'] ) || 'um_options' !== $_REQUEST['page'] ) ) {
|
||||
?>
|
||||
<div class="warning notice">
|
||||
<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.', 'um-stripe' );
|
||||
?>
|
||||
</p>
|
||||
<p>
|
||||
<a class="button button-primary" href="<?php echo esc_attr( admin_url( 'admin.php?page=um_options&tab=secure&um_dismiss_security_first_time_notice=1' ) ); ?>"><?php esc_html_e( 'Manage Security Settings', 'ultimate-member' ); ?></a>
|
||||
<a class="button" href="https://docs.ultimatemember.com/article/1869-security-feature"><?php esc_html_e( 'Read the documentation', 'ultimate-member' ); ?></a>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
// phpcs:enable WordPress.Security.NonceVerification
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user