From a2a56c8af76a7f6f8f521fe76d848649496be67f Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Sat, 1 Jul 2023 17:48:45 +0800 Subject: [PATCH 001/101] Add secure class for security measures --- includes/class-init.php | 15 ++ includes/core/class-secure.php | 279 +++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 includes/core/class-secure.php diff --git a/includes/class-init.php b/includes/class-init.php index 3c839c9a..7a3a0c27 100644 --- a/includes/class-init.php +++ b/includes/class-init.php @@ -607,6 +607,7 @@ if ( ! class_exists( 'UM' ) ) { $this->gdpr(); $this->member_directory(); $this->blocks(); + $this->secure(); //if multisite networks active if ( is_multisite() ) { @@ -650,6 +651,20 @@ if ( ! class_exists( 'UM' ) ) { } + /** + * @since 2.6.7 + * + * @return um\core\Secure() + */ + public function secure() { + if ( empty( $this->classes['secure'] ) ) { + $this->classes['secure'] = new um\core\Secure(); + } + + return $this->classes['secure']; + } + + /** * Get extension API * diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php new file mode 100644 index 00000000..f6f40c88 --- /dev/null +++ b/includes/core/class-secure.php @@ -0,0 +1,279 @@ +drop_sessions(); + wp_safe_redirect( admin_url() ); + exit; + } + + } + + /** + * Add Login notice for Reset Password + * + * @param array $args + * @since 2.6.7 + */ + public function reset_password_notice( $args ) { + + 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 "

"; + echo wp_kses( + sprintf( + // translators: One-time change requires you to reset your password + __( 'Important: Your password has expired. This (one-time) change requires you to reset your password. Please click here to reset your password via Email.', 'ultimate-member' ), + um_get_core_page( 'password-reset' ) + ), + array( + 'strong' => array(), + 'a' => array( + 'href' => array(), + ), + ) + ); + echo '

'; + } + + /** + * Add Login notice for Under Maintance + * + * @param array $args + * @since 2.6.7 + */ + public function under_maintanance_notice( $args ) { + + if ( ! UM()->options()->get( 'lock_register_forms' ) ) { + return; + } + + // phpcs:disable WordPress.Security.NonceVerification + if ( ! isset( $_REQUEST['notice'] ) || 'maintanance' !== $_REQUEST['notice'] ) { + return; + } + // phpcs:enable WordPress.Security.NonceVerification + + echo "

"; + echo wp_kses( + sprintf( + // translators: One-time change requires you to reset your password + __( 'Important: This site is currently under maintance. Come back soon.', 'ultimate-member' ), + um_get_core_page( 'password-reset' ) + ), + array( + 'strong' => array(), + 'a' => array( + 'href' => array(), + ), + ) + ); + echo '

'; + } + + /** + * Register Secure Settings + * + * @param array $settings + * @since 2.6.7 + */ + public function add_settings( $settings ) { + $nonce = wp_create_nonce( 'um-secure-expire-session-nonce' ); + $count_users = count_users(); + $settings['secure']['title'] = __( 'Secure', 'ultimate-member' ); + $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 temporarily.', 'ultimate-member' ), + ), + array( + 'id' => 'display_login_form_notice', + 'type' => 'checkbox', + 'label' => __( 'Display Login form notice to reset their 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' => 'Logout Users(' . esc_attr( $count_users['total_users'] ) . ') ', + 'description' => __( 'This will logout all users on your site and forces them to reset passwords when "Display Login form notice to reset their passwords" is enabled.', 'ultimate-member' ), + ), + ), + ), + ); + + return $settings; + } + + /** + * Block all UM Register form submissions. + * + * @param array $args Form settings. + * @since 2.6.7 + */ + 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' ) ); + nocache_headers(); + wp_safe_redirect( $login_url ); + exit; + } + } + + /** + * Validate when user has expired password + * + * @param array $submitted_data + * @since 2.6.7 + */ + public function login_validate_expired_pass( $submitted_data ) { + + 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 ) { + $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 Password Reset form + * + * @param object $errors + * @param object $user + * @since 2.6.7 + */ + public function avoid_old_password( $errors, $user ) { + $wp_hasher = new \PasswordHash( 8, true ); + + 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 ) ) { + UM()->form()->add_error( 'user_password', __( 'Your new password cannot be same as old password.', 'ultimate-member' ) ); + $errors->add( 'invalid_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' ) ); + } + } + } + + /** + * Checks user capabilities and remove administrative ones + */ + public function check_user_capabilities( $user_id, $submitted_data, $form_data ) { + + // Fetch the WP_User object of our user. + um_fetch_user( $user_id ); + $user = new \WP_User( $user_id ); + $has_admin_cap = false; + $disallowed_roles = array( 'administrator', 'editor' ); + foreach ( $disallowed_roles as $role ) { + $admin_caps = get_role( $role )->capabilities; + foreach ( $admin_caps as $cap ) { + if ( user_can( $user_id, $cap ) ) { + $has_admin_cap = true; + $this->revoke_caps( $cap, $user ); + } + } + } + + if ( user_can( $user_id, 'manage_options' ) ) { + $this->revoke_caps( $cap, $user ); + $has_admin_cap = true; + } + + $arr_levels = array( 'level_10', 'level_9', 'level_8' ); + foreach ( $arr_levels as $level ) { + if ( user_can( $user_id, $level ) ) { + $this->revoke_caps( $level, $user ); + $has_admin_cap = true; + } + } + + if ( $has_admin_cap ) { + wp_die( esc_html__( 'Security Check!', 'ultimate-member' ) ); + } + } + + /** + * Revoka Caps + * + * @param string $cap Capability slug + * @param object $user \WP_User + */ + public function revoke_caps( $cap, $user ) { + $user->remove_cap( $cap ); //revoke editable capabilities + $user->set_role( 'rejected' ); // Set role to rejected + UM()->user()->set_status( 'rejected' ); // Set UM role to rejected + } + + } + + +} From 77cd63ca75e3af8952fafa799c1fb92a2b941385 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Sat, 1 Jul 2023 17:52:55 +0800 Subject: [PATCH 002/101] Tweak cap --- includes/core/class-secure.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index f6f40c88..948d393b 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -244,7 +244,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { } if ( user_can( $user_id, 'manage_options' ) ) { - $this->revoke_caps( $cap, $user ); + $this->revoke_caps( 'manage_options', $user ); $has_admin_cap = true; } From fefdc5ecb8ccef4ae5e4b04c784628b79142b0b2 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Sat, 1 Jul 2023 18:02:16 +0800 Subject: [PATCH 003/101] Set array_keys to expose capabilities slugs --- includes/core/class-secure.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 948d393b..86dd3d7f 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -234,8 +234,8 @@ if ( ! class_exists( 'um\core\Secure' ) ) { $has_admin_cap = false; $disallowed_roles = array( 'administrator', 'editor' ); foreach ( $disallowed_roles as $role ) { - $admin_caps = get_role( $role )->capabilities; - foreach ( $admin_caps as $cap ) { + $admin_caps = array_keys( get_role( $role )->capabilities ); + foreach ( $admin_caps as $i => $cap ) { if ( user_can( $user_id, $cap ) ) { $has_admin_cap = true; $this->revoke_caps( $cap, $user ); From 5c1b6d99e1b094c19b416420d0a4011424afd308 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Sat, 1 Jul 2023 18:19:02 +0800 Subject: [PATCH 004/101] Tweak text --- includes/core/class-secure.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 86dd3d7f..15ae12db 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -113,7 +113,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { echo wp_kses( sprintf( // translators: One-time change requires you to reset your password - __( 'Important: This site is currently under maintance. Come back soon.', 'ultimate-member' ), + __( 'Important: This site is currently under maintenance. Please check back soon.', 'ultimate-member' ), um_get_core_page( 'password-reset' ) ), array( From e4fd2a8783e2ec7edd79dd4212b4cdbca2c8e14f Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Sat, 1 Jul 2023 14:44:32 +0300 Subject: [PATCH 005/101] - updated readme.txt --- ultimate-member.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ultimate-member.php b/ultimate-member.php index 032244b2..74c5fd2e 100644 --- a/ultimate-member.php +++ b/ultimate-member.php @@ -3,7 +3,7 @@ Plugin Name: Ultimate Member Plugin URI: http://ultimatemember.com/ Description: The easiest way to create powerful online communities and beautiful user profiles with WordPress -Version: 2.6.7 +Version: 2.6.8-alpha Author: Ultimate Member Author URI: http://ultimatemember.com/ Text Domain: ultimate-member From 12167f8612e439529f97f9c5e0b65a0e8321b79f Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Sun, 2 Jul 2023 12:58:22 +0300 Subject: [PATCH 006/101] - fixed updating user description if there isn't custom field on profile form, but field is displayed on profile top; --- includes/core/class-form.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/includes/core/class-form.php b/includes/core/class-form.php index e5b07c09..03eef6ae 100644 --- a/includes/core/class-form.php +++ b/includes/core/class-form.php @@ -493,6 +493,16 @@ if ( ! class_exists( 'um\core\Form' ) ) { $cf_metakeys[] = 'profile_photo'; $cf_metakeys[] = 'cover_photo'; + + if ( array_key_exists( 'show_bio', $this->form_data ) ) { + if ( ! emtpy( $this->form_data['show_bio'] ) ) { + $cf_metakeys[] = UM()->profile()->get_show_bio_key( $this->form_data ); + } + } else { + if ( UM()->options()->get( 'profile_show_bio' ) ) { + $cf_metakeys[] = UM()->profile()->get_show_bio_key( $this->form_data ); + } + } } // Add required usermeta for register. if ( 'register' === $this->form_data['mode'] ) { From 8e108c9443c54d12f52824d955d571b84150f5f2 Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Sun, 2 Jul 2023 13:39:42 +0300 Subject: [PATCH 007/101] - deprecated unnecessary `um_multiselect_option_value` hook; - updated hookdocs; --- includes/core/class-fields.php | 30 ++++-------------------- includes/core/class-member-directory.php | 11 +-------- includes/core/um-filters-fields.php | 22 ++--------------- 3 files changed, 8 insertions(+), 55 deletions(-) diff --git a/includes/core/class-fields.php b/includes/core/class-fields.php index 687e940a..0a1ae353 100644 --- a/includes/core/class-fields.php +++ b/includes/core/class-fields.php @@ -3235,13 +3235,15 @@ if ( ! class_exists( 'um\core\Fields' ) ) { /** * Filters enable options pair by field $data. * - * @since 2.0 + * @since 1.3.x `um_multiselect_option_value` + * @since 2.0 renamed to `um_select_options_pair` + * * @hook um_select_options_pair * * @param {bool|null} $options_pair Enable pairs. * @param {array} $data Field Data. * - * @return {bool} Enable pairs. + * @return {bool} Enable pairs. Set to `true` if a field requires text keys. * * @example Enable options pair. * function my_um_select_options_pair( $options_pair, $data ) { @@ -3334,28 +3336,6 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $output .= $this->field_label( $data['label'], $key, $data ); } - /** - * Filters multiselect keyword data. - * - * @since 1.3.x - * @hook um_multiselect_option_value - * - * @param {int} $keyword If 1 - keyword is enabled. It's 0 by default. - * @param {string} $type Field type. Deprecated since 2.6.7 - * @param {array} $data Field data. Added since 2.6.7 - * - * @return {int} Enabled keyword. - * - * @example Change multiselect keyword data. Enable it. - * function my_multiselect_option_value( $keyword, $data ) { - * // your code here - * $keyword = 1; - * return $keyword; - * } - * add_filter( 'um_multiselect_option_value', 'my_multiselect_option_value', 10, 2 ); - */ - $use_keyword = apply_filters( 'um_multiselect_option_value', 0, $data ); - $has_icon = ! empty( $data['icon'] ) && isset( $this->field_icons ) && 'field' === $this->field_icons; $output .= '
'; @@ -3363,7 +3343,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $output .= '
'; } - $output .= ''; if ( isset( $options ) && 'builtin' === $options ) { $options = UM()->builtin()->get( $data['filter'] ); diff --git a/includes/core/class-member-directory.php b/includes/core/class-member-directory.php index 3d8211cc..422e2f23 100644 --- a/includes/core/class-member-directory.php +++ b/includes/core/class-member-directory.php @@ -676,16 +676,7 @@ if ( ! class_exists( 'um\core\Member_Directory' ) ) { $attrs['options'] = UM()->fields()->get_options_from_callback( $attrs, $attrs['type'] ); } else { - /** - * UM hook - * - * @type filter - * @title um_select_option_value - * @description Enable options pair by field $data - * @input_vars - * [{"var":"$options_pair","type":"null","desc":"Enable pairs"}, - * {"var":"$data","type":"array","desc":"Field Data"}] - */ + /** This filter is documented in includes/core/class-fields.php */ $option_pairs = apply_filters( 'um_select_options_pair', null, $attrs ); } diff --git a/includes/core/um-filters-fields.php b/includes/core/um-filters-fields.php index 12101d31..e646f8cd 100644 --- a/includes/core/um-filters-fields.php +++ b/includes/core/um-filters-fields.php @@ -824,16 +824,7 @@ function um_profile_field_filter_xss_validation( $value, $data, $type = '' ) { } } elseif ( 'select' == $type || 'radio' == $type ) { - /** - * UM hook - * - * @type filter - * @title um_select_option_value - * @description Enable options pair by field $data - * @input_vars - * [{"var":"$options_pair","type":"null","desc":"Enable pairs"}, - * {"var":"$data","type":"array","desc":"Field Data"}] - */ + /** This filter is documented in includes/core/class-fields.php */ $option_pairs = apply_filters( 'um_select_options_pair', null, $data ); $array = empty( $data['options'] ) ? array() : $data['options']; @@ -859,16 +850,7 @@ function um_profile_field_filter_xss_validation( $value, $data, $type = '' ) { } elseif ( ! empty( $value ) && is_array( $value ) ) { if ( 'multiselect' == $type || 'checkbox' == $type ) { - /** - * UM hook - * - * @type filter - * @title um_select_option_value - * @description Enable options pair by field $data - * @input_vars - * [{"var":"$options_pair","type":"null","desc":"Enable pairs"}, - * {"var":"$data","type":"array","desc":"Field Data"}] - */ + /** This filter is documented in includes/core/class-fields.php */ $option_pairs = apply_filters( 'um_select_options_pair', null, $data ); $arr = $data['options']; From 7cc78e042d79ee531a6650a9e1012f5536210a5c Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Sun, 2 Jul 2023 20:17:18 +0300 Subject: [PATCH 008/101] - added $form_data attribute to 'um_before_save_registration_details' hook; --- includes/core/class-user.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/core/class-user.php b/includes/core/class-user.php index 914f1814..5da5373c 100644 --- a/includes/core/class-user.php +++ b/includes/core/class-user.php @@ -1409,14 +1409,15 @@ if ( ! class_exists( 'um\core\User' ) ) { * * @param {int} $user_id User ID. * @param {array} $submitted_data $_POST Submission array. + * @param {array} $form_data Form data. Since 2.6.8. * * @example Make any custom action before save registration details to the user. - * function my_before_save_registration_details( $user_id, $submitted_data ) { + * function my_before_save_registration_details( $user_id, $submitted_data, $form_data ) { * // your code here * } - * add_action( 'um_before_save_registration_details', 'my_before_save_registration_details', 10, 2 ); + * add_action( 'um_before_save_registration_details', 'my_before_save_registration_details', 10, 3 ); */ - do_action( 'um_before_save_registration_details', $this->id, $submitted ); + do_action( 'um_before_save_registration_details', $this->id, $submitted, $form_data ); update_user_meta( $this->id, 'submitted', $submitted ); From dae94dd363f3d33797d9d1c7ba1f86802453b7de Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Sun, 2 Jul 2023 20:43:30 +0300 Subject: [PATCH 009/101] - added hook 'um_submitted_data_value' for changing format of submitted data; --- includes/um-short-functions.php | 47 ++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/includes/um-short-functions.php b/includes/um-short-functions.php index c8951eac..11504767 100644 --- a/includes/um-short-functions.php +++ b/includes/um-short-functions.php @@ -844,7 +844,7 @@ function um_user_submitted_registration_formatted( $style = false ) { function um_user_submited_display( $k, $title, $data = array(), $style = true ) { $output = ''; - if ( 'form_id' == $k && isset( $data['form_id'] ) && ! empty( $data['form_id'] ) ) { + if ( 'form_id' === $k && ! empty( $data['form_id'] ) ) { $v = sprintf( __( '%s - Form ID#: %s', 'ultimate-member' ), get_the_title( $data['form_id'] ), $data['form_id'] ); } else { $v = um_user( $k ); @@ -855,23 +855,22 @@ function um_user_submited_display( $k, $title, $data = array(), $style = true ) } $fields_without_metakey = UM()->builtin()->get_fields_without_metakey(); - $type = UM()->fields()->get_field_type( $k ); - if ( in_array( $type, $fields_without_metakey ) ) { + $type = UM()->fields()->get_field_type( $k ); + if ( in_array( $type, $fields_without_metakey, true ) ) { return ''; } if ( ! $v ) { if ( $style ) { - return "

" . __( '(empty)', 'ultimate-member' ) ."

"; - } else { - return ''; + return "

" . esc_html__( '(empty)', 'ultimate-member' ) . '

'; } + return ''; } - if ( $type == 'image' || $type == 'file' ) { + if ( in_array( $type, array( 'image', 'file' ), true ) ) { $file = basename( $v ); - $filedata = get_user_meta( um_user( 'ID' ), $k . "_metadata", true ); + $filedata = get_user_meta( um_user( 'ID' ), $k . '_metadata', true ); $baseurl = UM()->uploader()->get_upload_base_url(); if ( ! file_exists( UM()->uploader()->get_upload_base_dir() . um_user( 'ID' ) . DIRECTORY_SEPARATOR . $file ) ) { @@ -888,14 +887,38 @@ function um_user_submited_display( $k, $title, $data = array(), $style = true ) } } + /** + * Filters submitted data info before displaying in modal window or submit to admin email. + * + * @param {string|array} $value Submitted value. + * @param {string} $k Submitted data metakey. + * @param {array} $data Submitted data + * @param {bool} $style If styled echo + * + * @return {string|array} Is allowed verify. + * + * @since 2.6.8 + * @hook um_submitted_data_value + * + * @example Change submitted data info before echo. + * function my_um_submitted_data_value ( $value, $metakey, $data, $style ) { + * if ( 'some_metakey' === $metakey ) { + * $value = 'new_value'; + * } + * return $value; + * } + * add_filter( 'um_submitted_data_value', 'my_um_submitted_data_value', 10, 4 ); + */ + $v = apply_filters( 'um_submitted_data_value', $v, $k, $data, $style ); + if ( is_array( $v ) ) { $v = implode( ',', $v ); } - if ( $k == 'timestamp' ) { - $v = date( "d M Y H:i", $v ); - } elseif ( $k == 'use_gdpr_agreement' ) { - $v = date( "d M Y H:i", $v ); + if ( 'timestamp' === $k ) { + $v = wp_date( get_option( 'date_format', 'Y-m-d' ) . ' ' . get_option( 'time_format', 'H:i:s' ), $v ); + } elseif ( 'use_gdpr_agreement' === $k ) { + $v = wp_date( get_option( 'date_format', 'Y-m-d' ) . ' ' . get_option( 'time_format', 'H:i:s' ), $v ); } if ( $style ) { From 77889c6070a47110fc401adbe1381661022f1bad Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Sun, 2 Jul 2023 20:45:09 +0300 Subject: [PATCH 010/101] - fixed typo; --- includes/core/class-form.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-form.php b/includes/core/class-form.php index 03eef6ae..14c88f4f 100644 --- a/includes/core/class-form.php +++ b/includes/core/class-form.php @@ -495,7 +495,7 @@ if ( ! class_exists( 'um\core\Form' ) ) { $cf_metakeys[] = 'cover_photo'; if ( array_key_exists( 'show_bio', $this->form_data ) ) { - if ( ! emtpy( $this->form_data['show_bio'] ) ) { + if ( ! empty( $this->form_data['show_bio'] ) ) { $cf_metakeys[] = UM()->profile()->get_show_bio_key( $this->form_data ); } } else { From 6e9bd103b3fa692ff659bcbc536c4806f0b2b03e Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Sun, 2 Jul 2023 20:52:25 +0300 Subject: [PATCH 011/101] - fixed fatal already fixed in 2.6.7; --- includes/core/class-fields.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-fields.php b/includes/core/class-fields.php index 0a1ae353..76ed4f6d 100644 --- a/includes/core/class-fields.php +++ b/includes/core/class-fields.php @@ -2638,7 +2638,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $field_name = $key . $form_suffix; $disabled_weekdays = ''; - if ( isset( $data['disabled_weekdays'] ) ) { + if ( isset( $data['disabled_weekdays'] ) && is_array( $data['disabled_weekdays'] ) ) { $disabled_weekdays = '[' . implode( ',', $data['disabled_weekdays'] ) . ']'; } From 5fa9b23233fcf5cff49e844c4fe17a8b45c3f7ff Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Sun, 2 Jul 2023 21:21:50 +0300 Subject: [PATCH 012/101] - fixed registration with empty role field (set default if empty); --- includes/core/class-form.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/includes/core/class-form.php b/includes/core/class-form.php index 14c88f4f..5a117136 100644 --- a/includes/core/class-form.php +++ b/includes/core/class-form.php @@ -590,7 +590,7 @@ if ( ! class_exists( 'um\core\Form' ) ) { unset( $this->post_form['role'] ); } else { $custom_field_roles = $this->custom_field_roles( $this->form_data['custom_fields'] ); - if ( ! empty( $custom_field_roles ) ) { + if ( ! empty( $custom_field_roles ) && ! empty( $this->post_form['role'] ) ) { if ( is_array( $this->post_form['role'] ) ) { $role = current( $this->post_form['role'] ); $role = sanitize_key( $role ); @@ -965,9 +965,10 @@ if ( ! class_exists( 'um\core\Form' ) ) { if ( strstr( $field_key, 'role_' ) && array_key_exists( 'options', $field_settings ) && is_array( $field_settings['options'] ) ) { - if ( isset( $this->post_form['mode'] ) && 'profile' === $this->post_form['mode'] && - isset( $field_settings['editable'] ) && $field_settings['editable'] == 0 ) { - continue; + if ( isset( $this->post_form['mode'] ) && 'profile' === $this->post_form['mode'] ) { + if ( empty( $field_settings['editable'] ) || ! um_can_edit_field( $field_settings ) ) { + continue; + } } if ( ! um_can_view_field( $field_settings ) ) { From 9b3447ab54d076c86932619bc267b664fb42ed07 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Mon, 3 Jul 2023 15:38:29 +0800 Subject: [PATCH 013/101] Initial update --- includes/core/class-secure.php | 73 ++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 15ae12db..baf9ccea 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -5,7 +5,6 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } - if ( ! class_exists( 'um\core\Secure' ) ) { /** @@ -13,13 +12,13 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * * @package um\core * - * @since 2.6.7 + * @since 2.6.8 */ class Secure { /** * Login constructor. - * @since 2.6.7 + * @since 2.6.8 */ public function __construct() { add_action( 'admin_init', array( $this, 'admin_init' ) ); @@ -36,9 +35,14 @@ if ( ! class_exists( 'um\core\Secure' ) ) { add_action( 'validate_password_reset', array( $this, 'avoid_old_password' ), 1, 2 ); - add_action( 'um_after_save_registration_details', array( $this, 'check_user_capabilities' ), 10, 3 ); + add_action( 'um_after_save_registration_details', array( $this, 'secure_user_capabilities' ), 10, 3 ); } + /** + * Admin Init + * + * @since 2.6.8 + */ public function admin_init() { if ( isset( $_REQUEST['um_secure_expire_all_sessions'] ) ) { if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'um-secure-expire-session-nonce' ) ) { @@ -60,7 +64,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * Add Login notice for Reset Password * * @param array $args - * @since 2.6.7 + * @since 2.6.8 */ public function reset_password_notice( $args ) { @@ -95,7 +99,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * Add Login notice for Under Maintance * * @param array $args - * @since 2.6.7 + * @since 2.6.8 */ public function under_maintanance_notice( $args ) { @@ -130,7 +134,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * Register Secure Settings * * @param array $settings - * @since 2.6.7 + * @since 2.6.8 */ public function add_settings( $settings ) { $nonce = wp_create_nonce( 'um-secure-expire-session-nonce' ); @@ -172,7 +176,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * Block all UM Register form submissions. * * @param array $args Form settings. - * @since 2.6.7 + * @since 2.6.8 */ public function block_register_forms( $args ) { if ( UM()->options()->get( 'lock_register_forms' ) ) { @@ -187,7 +191,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * Validate when user has expired password * * @param array $submitted_data - * @since 2.6.7 + * @since 2.6.8 */ public function login_validate_expired_pass( $submitted_data ) { @@ -206,7 +210,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * * @param object $errors * @param object $user - * @since 2.6.7 + * @since 2.6.8 */ public function avoid_old_password( $errors, $user ) { $wp_hasher = new \PasswordHash( 8, true ); @@ -215,7 +219,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { $new_user_pass = $_REQUEST['user_password']; // phpcs:ignore WordPress.Security.NonceVerification if ( $wp_hasher->CheckPassword( $new_user_pass, $user->data->user_pass ) ) { UM()->form()->add_error( 'user_password', __( 'Your new password cannot be same as old password.', 'ultimate-member' ) ); - $errors->add( 'invalid_old_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' ) ); } 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' ) ); @@ -224,35 +228,43 @@ if ( ! class_exists( 'um\core\Secure' ) ) { } /** - * Checks user capabilities and remove administrative ones + * Secure user capabilities and revoke administrative ones */ - public function check_user_capabilities( $user_id, $submitted_data, $form_data ) { - + public function secure_user_capabilities( $user_id, $submitted_data, $form_data ) { + global $wpdb; // Fetch the WP_User object of our user. um_fetch_user( $user_id ); $user = new \WP_User( $user_id ); $has_admin_cap = false; - $disallowed_roles = array( 'administrator', 'editor' ); + $disallowed_roles = array( 'administrator' ); foreach ( $disallowed_roles as $role ) { $admin_caps = array_keys( get_role( $role )->capabilities ); foreach ( $admin_caps as $i => $cap ) { - if ( user_can( $user_id, $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( $cap, $user ); + $this->revoke_caps( $user ); + break; } } } - if ( user_can( $user_id, 'manage_options' ) ) { - $this->revoke_caps( 'manage_options', $user ); - $has_admin_cap = true; - } - - $arr_levels = array( 'level_10', 'level_9', 'level_8' ); - foreach ( $arr_levels as $level ) { - if ( user_can( $user_id, $level ) ) { - $this->revoke_caps( $level, $user ); - $has_admin_cap = true; + /** + * Double-check if *_user_level has been modified with the highest level + * when user has no administrator capabilities. + */ + $user_level = um_user( $wpdb->get_blog_prefix() . 'user_level' ); + if ( ! empty( $user_level ) ) { + $arr_levels = array( 'level_10' ); + foreach ( $arr_levels as $level ) { + if ( $level === $user_level ) { + $this->revoke_caps( $user ); + $has_admin_cap = true; + break; + } } } @@ -262,13 +274,14 @@ if ( ! class_exists( 'um\core\Secure' ) ) { } /** - * Revoka Caps + * Revoke Caps * * @param string $cap Capability slug * @param object $user \WP_User */ - public function revoke_caps( $cap, $user ) { - $user->remove_cap( $cap ); //revoke editable capabilities + public function revoke_caps( $user ) { + + $user->remove_all_caps(); $user->set_role( 'rejected' ); // Set role to rejected UM()->user()->set_status( 'rejected' ); // Set UM role to rejected } From 1313d592518f2017afdc31df58f215a53f651354 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Mon, 3 Jul 2023 16:48:36 +0800 Subject: [PATCH 014/101] Add banned capabilities --- includes/core/class-secure.php | 110 ++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 22 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index baf9ccea..3eaa14e4 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -16,6 +16,15 @@ if ( ! class_exists( 'um\core\Secure' ) ) { */ class Secure { + /** + * Banned Administrative Capabilities + * + * @since 2.6.8 + * + * @var array + */ + private $banned_admin_capabilities = array(); + /** * Login constructor. * @since 2.6.8 @@ -35,6 +44,70 @@ 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 + * + * @example + * + */ + $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', + 'manage_options', + 'promote_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', + ) + ); + add_action( 'um_after_save_registration_details', array( $this, 'secure_user_capabilities' ), 10, 3 ); } @@ -150,12 +223,12 @@ if ( ! class_exists( 'um\core\Secure' ) ) { '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 temporarily.', '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 their passwords', 'ultimate-member' ), + '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( @@ -163,7 +236,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { 'type' => 'info_text', 'label' => __( 'Expire All Users Sessions', 'ultimate-member' ), 'value' => 'Logout Users(' . esc_attr( $count_users['total_users'] ) . ') ', - 'description' => __( 'This will logout all users on your site and forces them to reset passwords when "Display Login form notice to reset their passwords" is enabled.', 'ultimate-member' ), + 'description' => __( 'This will logout all users on your site and forces them to reset passwords
when "Display Login form notice to reset passwords" is enabled/checked.', 'ultimate-member' ), ), ), ), @@ -233,13 +306,11 @@ if ( ! class_exists( 'um\core\Secure' ) ) { public function secure_user_capabilities( $user_id, $submitted_data, $form_data ) { global $wpdb; // Fetch the WP_User object of our user. - um_fetch_user( $user_id ); - $user = new \WP_User( $user_id ); - $has_admin_cap = false; - $disallowed_roles = array( 'administrator' ); - foreach ( $disallowed_roles as $role ) { - $admin_caps = array_keys( get_role( $role )->capabilities ); - foreach ( $admin_caps as $i => $cap ) { + um_fetch_user( 29 ); + $user = new \WP_User( 29 ); + $has_admin_cap = false; + if ( ! empty( $this->banned_admin_capabilities ) ) { + foreach ( $this->banned_admin_capabilities as $i => $cap ) { /** * When there's at least one administrator cap added to the user, * immediately revoke caps and mark as rejected. @@ -254,17 +325,13 @@ if ( ! class_exists( 'um\core\Secure' ) ) { /** * Double-check if *_user_level has been modified with the highest level - * when user has no administrator capabilities. + * when user has no administrative capabilities. */ $user_level = um_user( $wpdb->get_blog_prefix() . 'user_level' ); if ( ! empty( $user_level ) ) { - $arr_levels = array( 'level_10' ); - foreach ( $arr_levels as $level ) { - if ( $level === $user_level ) { - $this->revoke_caps( $user ); - $has_admin_cap = true; - break; - } + if ( 10 === $user_level ) { + $this->revoke_caps( $user ); + $has_admin_cap = true; } } @@ -274,16 +341,15 @@ if ( ! class_exists( 'um\core\Secure' ) ) { } /** - * Revoke Caps + * Revoke Caps & Mark rejected as suspicious * * @param string $cap Capability slug * @param object $user \WP_User */ public function revoke_caps( $user ) { - $user->remove_all_caps(); - $user->set_role( 'rejected' ); // Set role to rejected - UM()->user()->set_status( 'rejected' ); // Set UM role to rejected + UM()->user()->set_status( 'rejected' ); + update_user_meta( $user->get( 'ID' ), 'um_user_blocked', 'suspicious_activity' ); } } From a7fe0b17f08158d0387fb8ea7e2e142f5faacb02 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Mon, 3 Jul 2023 16:54:16 +0800 Subject: [PATCH 015/101] Remove test account --- includes/core/class-secure.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 3eaa14e4..e6c79fc1 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -306,8 +306,8 @@ if ( ! class_exists( 'um\core\Secure' ) ) { public function secure_user_capabilities( $user_id, $submitted_data, $form_data ) { global $wpdb; // Fetch the WP_User object of our user. - um_fetch_user( 29 ); - $user = new \WP_User( 29 ); + um_fetch_user( $user_id ); + $user = new \WP_User( $user_id ); $has_admin_cap = false; if ( ! empty( $this->banned_admin_capabilities ) ) { foreach ( $this->banned_admin_capabilities as $i => $cap ) { From 1a2f683645373ecc2e25060eaca3e1a6e0649c05 Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Mon, 3 Jul 2023 14:19:42 +0300 Subject: [PATCH 016/101] - wpcs; --- includes/core/class-fields.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-fields.php b/includes/core/class-fields.php index 76ed4f6d..667f6be6 100644 --- a/includes/core/class-fields.php +++ b/includes/core/class-fields.php @@ -4843,7 +4843,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $fields_without_metakey = UM()->builtin()->get_fields_without_metakey(); - if ( in_array( $data['type'], $fields_without_metakey ) ) { + if ( in_array( $data['type'], $fields_without_metakey, true ) ) { unset( $field_atts['id'] ); if ( empty( $field_atts['data-key'] ) ) { From 2ac32d921d75e65bbf0f5312d75e009ef0ba4939 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Mon, 3 Jul 2023 19:38:16 +0800 Subject: [PATCH 017/101] Add blocked status & time in the account status column --- includes/core/class-secure.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index e6c79fc1..058fc7fe 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -109,6 +109,8 @@ if ( ! class_exists( 'um\core\Secure' ) ) { ); add_action( 'um_after_save_registration_details', array( $this, 'secure_user_capabilities' ), 10, 3 ); + + add_filter( 'manage_users_custom_column', array( $this, 'manage_users_custom_column' ), 10, 3 ); } /** @@ -302,6 +304,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { /** * Secure user capabilities and revoke administrative ones + * @since 2.6.8 */ public function secure_user_capabilities( $user_id, $submitted_data, $form_data ) { global $wpdb; @@ -345,11 +348,39 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * * @param string $cap Capability slug * @param object $user \WP_User + * + * @since 2.6.8 */ public function revoke_caps( $user ) { $user->remove_all_caps(); 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' ) ); + } + + /** + * 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 ) { + $is_blocked = get_user_meta( $user_id, 'um_user_blocked', true ); + if ( ! empty( $is_blocked ) ) { + $datetime = get_user_meta( $user_id, 'um_user_blocked__datetime', true ); + $val = $val . '
' . __( 'Blocked Due to Suspicious Activity', 'ultimate-member' ) . '
'; + if ( ! empty( $datetime ) ) { + $val = $val . '
' . human_time_diff( strtotime( $datetime ), strtotime( current_time( 'mysql' ) ) ) . ' ' . __( 'ago', 'ultimate-member' ) . '
'; + } + } + } + return $val; } } From 85b08f45b35253e246624b8452f34e13bc457e43 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Mon, 3 Jul 2023 23:28:49 +0800 Subject: [PATCH 018/101] Add Notification with Schedules --- includes/core/class-secure.php | 305 ++++++++++++++++++++++++++++++--- 1 file changed, 284 insertions(+), 21 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 058fc7fe..a2343cf8 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -1,6 +1,8 @@ banned_admin_capabilities as $i => $cap ) { + if ( in_array( $cap, array( 'manage_options', 'promote_users', 'level_10' ), true ) ) { + continue; + } + $banned_admin_capabilities_options[ $cap ] = $cap; + } + $settings['secure']['sections'] = array( '' => @@ -240,6 +282,33 @@ if ( ! class_exists( 'um\core\Secure' ) ) { 'value' => 'Logout Users(' . esc_attr( $count_users['total_users'] ) . ') ', 'description' => __( 'This will logout all users on your site and forces them to reset passwords
when "Display Login form notice to reset passwords" is enabled/checked.', 'ultimate-member' ), ), + array( + 'id' => 'banned_capabilities', + 'type' => 'multi_checkbox', + 'multi' => true, + 'columns' => 2, + 'options' => $banned_admin_capabilities_options, + 'value' => UM()->options()->get( 'banned_capabilities' ) ? array_keys( UM()->options()->get( 'banned_capabilities' ) ) : array_keys( $banned_admin_capabilities_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 Profile & Register form submission, it will be flagged with this option. The manage_options, promote_users & level_10 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 ), + ), ), ), ); @@ -306,23 +375,33 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * Secure user capabilities and revoke administrative ones * @since 2.6.8 */ - public function secure_user_capabilities( $user_id, $submitted_data, $form_data ) { + public function secure_user_capabilities( $user_id ) { global $wpdb; // Fetch the WP_User object of our user. um_fetch_user( $user_id ); - $user = new \WP_User( $user_id ); - $has_admin_cap = false; - if ( ! empty( $this->banned_admin_capabilities ) ) { - foreach ( $this->banned_admin_capabilities as $i => $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; - } + $user = new \WP_User( $user_id ); + $has_admin_cap = false; + $arr_banned_caps = array(); + + if ( UM()->options()->get( 'banned_capabilities' ) ) { + $arr_banned_caps = array_keys( UM()->options()->get( 'banned_capabilities' ) ); + } else { + $arr_banned_caps = $this->banned_admin_capabilities; + } + + // Add locked administratrive capabilities + $arr_banned_caps[] = 'manage_options'; + $arr_banned_caps[] = 'promote_users'; + + foreach ( $arr_banned_caps as $i => $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; } } @@ -339,7 +418,24 @@ if ( ! class_exists( 'um\core\Secure' ) ) { } if ( $has_admin_cap ) { - wp_die( esc_html__( 'Security Check!', 'ultimate-member' ) ); + /** + * 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' ) ) ); + } + } + + // Destroy Sessions & Redirect. + wp_destroy_current_session(); + wp_logout(); + session_unset(); + $login_url = add_query_arg( 'err', 'inactive', um_get_core_page( 'login' ) ); + wp_safe_redirect( $login_url ); + exit; + } } @@ -352,8 +448,17 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * @since 2.6.8 */ public function revoke_caps( $user ) { + $captured = array( + 'capabilities' => $user->allcaps, + 'submitted' => $_REQUEST, //phpcs:ignore WordPress.Security.NonceVerification + ); + update_user_meta( $user->get( 'ID' ), 'um_user_blocked__metadata', $captured ); $user->remove_all_caps(); - UM()->user()->set_status( 'rejected' ); + if ( is_user_logged_in() ) { + UM()->user()->set_status( 'inactive' ); + } 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' ) ); } @@ -383,6 +488,164 @@ if ( ! class_exists( 'um\core\Secure' ) ) { return $val; } + /** + * Register 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' ) ) { + wp_schedule_event( current_time( 'mysql' ), 'hourly', 'um_secure_notify_administrator_hourly' ); + wp_schedule_event( current_time( 'mysql' ), 'daily', 'um_secure_notify_administrator_daily' ); + } + } + } + + /** + * Notify Administrators hourly - Suspicious activities in an hour + * + * @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', + ), + ), + ); + + $users = new \WP_User_Query( $args ); + + $this->send_email( array_values( $users->get_results() ) ); + } + + } + + /** + * Notify Administrators daily - Today's suspicious activity + * + * @since 2.6.8 + */ + public function notify_administrators_daily() { + + $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', + ), + ), + ); + + $users = new \WP_User_Query( $args ); + + $this->send_email( array_values( $users->get_results() ) ); + + } + + } + + /** + * Get Email template + * + * @param bool $single Whether the template is for single or multiple user activities + * @param array $profile_urls Profile URLs to include in the email body + * + * @since 2.6.8 + */ + public function get_email_template( $single = true, $profile_urls = array() ) { + $action = ''; + if ( ! is_user_logged_in() ) { + $action = 'Rejected'; + } else { + $action = 'Deactivated'; + } + + $body = ''; + if ( $single ) { + $body = 'This is to inform you that there\'s a suspicious activity with the following account: '; + $body .= '
'; + $body .= '{user_profile_link}'; + $body .= '

'; + $body .= 'Due to that we have set the account status to ' . $action . ', Revoked Roles & Destroyed the Login Session.'; + $body .= '
'; + } else { + $body = 'This is to inform you that there are suspicious activities with the following accounts: '; + $body .= '
'; + $body .= '{user_profile_link}'; + $body .= '

'; + $body .= 'Due to that we have set each account\'s status to ' . $action . ', revoked roles & destroyed the login session.'; + $body .= '
'; + } + + $urls = implode( '
', $profile_urls ); + $body = str_replace( '{user_profile_link}', $urls, $body ); + $body .= '

- Sent via Ultimate Member plugin. '; + + return $body; + } + + /** + * Send Email + * + * @param array $user_ids User IDs. + * + * @since 2.6.8 + */ + public function send_email( $user_ids = array() ) { + + if ( empty( $user_ids ) ) { + return ''; + } + $multiple_recipients = array(); + $admins = get_users( 'role=Administrator' ); + foreach ( $admins as $user ) { + $multiple_recipients[] = $user->user_email; + } + + if ( count( $user_ids ) <= 1 ) { + $subject = __( 'Suspicious Account Activity on ', 'ultimate-member' ) . wp_parse_url( get_site_url() )['host']; + $url = UM()->user()->get_profile_link( $user_ids[0] ); + $body = $this->get_email_template( true, array( $url ) ); + } else { + $subject = __( 'Suspicious Accounts & Activities on ', 'ultimate-member' ) . wp_parse_url( get_site_url() )['host']; + $arr_urls = array(); + foreach ( $user_ids as $i => $uid ) { + $arr_urls[] = UM()->user()->get_profile_link( $uid ); + } + $body = $this->get_email_template( false, $arr_urls ); + } + + wp_mail( $multiple_recipients, $subject, $body ); + + } + } From 753db264f8565e62695a078ccff3fa33747fa8af Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Tue, 4 Jul 2023 00:18:36 +0800 Subject: [PATCH 019/101] Add filter hook for redirecting flagged user --- includes/core/class-secure.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index a2343cf8..d740f42a 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -432,11 +432,18 @@ if ( ! class_exists( 'um\core\Secure' ) ) { wp_destroy_current_session(); wp_logout(); session_unset(); - $login_url = add_query_arg( 'err', 'inactive', um_get_core_page( 'login' ) ); - wp_safe_redirect( $login_url ); - exit; + $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; + } + + return true; } + + return false; } /** From 42188baea6e7e81f770ef733b81d6caa2d5093dc Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Tue, 4 Jul 2023 00:37:17 +0800 Subject: [PATCH 020/101] Fix wp schedule event for email notifications --- includes/core/class-secure.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index d740f42a..117c8109 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -505,10 +505,15 @@ if ( ! class_exists( 'um\core\Secure' ) ) { 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' ) ) { + + if ( ! wp_next_scheduled( 'um_secure_notify_administrator_hourly' ) ) { wp_schedule_event( current_time( 'mysql' ), 'hourly', 'um_secure_notify_administrator_hourly' ); + } + + if ( ! wp_next_scheduled( 'um_secure_notify_administrator_daily' ) ) { wp_schedule_event( current_time( 'mysql' ), 'daily', 'um_secure_notify_administrator_daily' ); } + } } From e1c3950fb9f16b72628499840104b602ef53e2a5 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Tue, 4 Jul 2023 01:05:54 +0800 Subject: [PATCH 021/101] capture submitted data for flagged users --- includes/core/class-secure.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 117c8109..20fbd3e6 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -457,7 +457,8 @@ if ( ! class_exists( 'um\core\Secure' ) ) { public function revoke_caps( $user ) { $captured = array( 'capabilities' => $user->allcaps, - 'submitted' => $_REQUEST, //phpcs:ignore WordPress.Security.NonceVerification + 'submitted' => UM()->form()->post_form, + 'roles' => $user->roles, ); update_user_meta( $user->get( 'ID' ), 'um_user_blocked__metadata', $captured ); $user->remove_all_caps(); From 46720f0a3254c0a42c092b482e7a2bf7fa538a49 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Tue, 4 Jul 2023 01:42:27 +0800 Subject: [PATCH 022/101] Tweak property visibillity for WP CLI --- includes/core/class-secure.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 20fbd3e6..cc4fb300 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -1,8 +1,6 @@ Date: Tue, 4 Jul 2023 02:53:53 +0800 Subject: [PATCH 023/101] Refactor email subject --- includes/core/class-secure.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index cc4fb300..3948af22 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -512,7 +512,6 @@ if ( ! class_exists( 'um\core\Secure' ) ) { if ( ! wp_next_scheduled( 'um_secure_notify_administrator_daily' ) ) { wp_schedule_event( current_time( 'mysql' ), 'daily', 'um_secure_notify_administrator_daily' ); } - } } @@ -640,12 +639,12 @@ if ( ! class_exists( 'um\core\Secure' ) ) { $multiple_recipients[] = $user->user_email; } + $subject = _n( 'Suspicious Account Activity on ', 'Suspicious Accounts & Activities on ', count( $user_ids ), 'ultimate-member' ) . wp_parse_url( get_site_url() )['host']; + if ( count( $user_ids ) <= 1 ) { - $subject = __( 'Suspicious Account Activity on ', 'ultimate-member' ) . wp_parse_url( get_site_url() )['host']; - $url = UM()->user()->get_profile_link( $user_ids[0] ); - $body = $this->get_email_template( true, array( $url ) ); + $url = UM()->user()->get_profile_link( $user_ids[0] ); + $body = $this->get_email_template( true, array( $url ) ); } else { - $subject = __( 'Suspicious Accounts & Activities on ', 'ultimate-member' ) . wp_parse_url( get_site_url() )['host']; $arr_urls = array(); foreach ( $user_ids as $i => $uid ) { $arr_urls[] = UM()->user()->get_profile_link( $uid ); From 8dba1534a0ac553ca6e90eff414b4465ddd4f0f8 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Tue, 4 Jul 2023 03:34:19 +0800 Subject: [PATCH 024/101] Add account form to check capabilities integrity --- includes/core/class-secure.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 3948af22..ac5682cf 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -139,6 +139,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { add_action( 'um_after_save_registration_details', array( $this, 'secure_user_capabilities' ), 10 ); 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 ); } } From 85ebc8b83b113c244222cd792ca8f660b211fce2 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Tue, 4 Jul 2023 03:57:29 +0800 Subject: [PATCH 025/101] Include flagged user's browser data in the captured metadata --- includes/core/class-secure.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index ac5682cf..344b2937 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -454,10 +454,19 @@ if ( ! class_exists( 'um\core\Secure' ) ) { * @since 2.6.8 */ public function revoke_caps( $user ) { + + if ( ! class_exists( '\Browser' ) ) { + require_once um_path . 'includes/lib/browser.php'; + } + + // Detect browser + $browser = new \Browser(); + $captured = array( 'capabilities' => $user->allcaps, 'submitted' => UM()->form()->post_form, 'roles' => $user->roles, + 'user_browser' => $browser, ); update_user_meta( $user->get( 'ID' ), 'um_user_blocked__metadata', $captured ); $user->remove_all_caps(); From a7f3b80d3ef34e38da5859ff9bde391f333cdb72 Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Tue, 4 Jul 2023 12:44:06 +0300 Subject: [PATCH 026/101] - reviewd #1237; --- includes/core/class-fields.php | 102 ++++++++++++--------------- includes/core/class-validation.php | 11 +-- includes/core/um-filters-account.php | 42 ++++++----- 3 files changed, 77 insertions(+), 78 deletions(-) diff --git a/includes/core/class-fields.php b/includes/core/class-fields.php index 667f6be6..d1e8825e 100644 --- a/includes/core/class-fields.php +++ b/includes/core/class-fields.php @@ -1381,8 +1381,9 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( $has_custom_source ) { - $opts = apply_filters( "um_get_field__{$data['metakey']}", array() ); - $arr_options = $opts['options']; + /** This filter is documented in includes/core/class-fields.php */ + $opts = apply_filters( "um_get_field__{$data['metakey']}", array() ); + $arr_options = array_key_exists( 'options', $opts ) ? $opts['options'] : array(); } elseif ( function_exists( $data['custom_dropdown_options_source'] ) ) { if ( isset( $data['parent_dropdown_relationship'] ) ) { @@ -1627,7 +1628,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $fields_without_metakey = UM()->builtin()->get_fields_without_metakey(); - if ( ! in_array( $array['type'], $fields_without_metakey ) ) { + if ( ! in_array( $array['type'], $fields_without_metakey, true ) ) { $array['classes'] .= ' um-field-' . esc_attr( $key ); } $array['classes'] .= ' um-field-' . esc_attr( $array['type'] ); @@ -1647,9 +1648,8 @@ if ( ! class_exists( 'um\core\Fields' ) ) { case 'text': $array['disabled'] = ''; - - if ( $key == 'user_login' && isset( $this->set_mode ) && $this->set_mode == 'account' ) { - $array['disabled'] = 'disabled="disabled"'; + if ( 'user_login' === $key && 'account' === $this->set_mode ) { + $array['disabled'] = ' disabled="disabled" '; } $array['input'] = 'text'; @@ -1759,37 +1759,10 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $array['date_min'] = $past; $array['date_max'] = $future; - } - } - - /** - * UM hook - * - * @type filter - * @title um_get_field_date - * @description Extend field date - * @input_vars - * [{"var":"$data","type":"array","desc":"Field Date Data"}] - * @change_log - * ["Since: 2.5.4"] - * @usage add_filter( 'um_get_field_date', 'function_name', 10, 1 ); - * @example - * - */ - $array = apply_filters( 'um_get_field_date', $array ); - break; - case 'time': - $array['input'] = 'text'; if ( ! isset( $array['format'] ) ) { @@ -2019,31 +1992,45 @@ if ( ! class_exists( 'um\core\Fields' ) ) { } /** - * UM hook + * Filters the field data by the field type. Where $type is the field's type. * - * @type filter - * @title um_get_field__{$key} - * @description Extend field data by field $key - * @input_vars - * [{"var":"$data","type":"array","desc":"Field Data"}] - * @change_log - * ["Since: 2.0"] - * @usage add_filter( 'um_get_field__{$key}', 'function_name', 10, 1 ); - * @example - * Disable all date-type fields. + * function my_custom_get_field_date( $field_data ) { + * $field_data['disabled'] = ' disabled="disabled" '; + * return $field_data; * } - * ?> + * add_filter( 'um_get_field_date', 'my_custom_get_field_date' ); */ - $array = apply_filters( "um_get_field__{$key}", $array ); - - return $array; + $array = apply_filters( "um_get_field_{$array['type']}", $array ); + /** + * Filters the field data by the metakey. Where $key is the field's metakey. + * + * @param {array} $field_data Field data. + * + * @return {array} Field data. + * + * @since 1.3.x + * @hook um_get_field__{$key} + * + * @example Disable 'first_name' field. + * function my_custom_disable_first_name( $field_data ) { + * $field_data['disabled'] = ' disabled="disabled" '; + * return $field_data; + * } + * add_filter( 'um_get_field__first_name', 'my_custom_disable_first_name' ); + */ + return apply_filters( "um_get_field__{$key}", $array ); } - /** * @param $option_value * @@ -2109,8 +2096,8 @@ if ( ! class_exists( 'um\core\Fields' ) ) { $form_suffix = UM()->form()->form_suffix; } - $output = ''; - $disabled = ''; + $output = ''; + if ( empty( $_um_profile_id ) ) { $_um_profile_id = um_user( 'ID' ); } @@ -2133,6 +2120,11 @@ if ( ! class_exists( 'um\core\Fields' ) ) { } $type = $data['type']; + $disabled = ''; + if ( isset( $data['disabled'] ) ) { + $disabled = $data['disabled']; + } + if ( isset( $data['in_group'] ) && '' !== $data['in_group'] && 'group' !== $rule ) { return ''; } diff --git a/includes/core/class-validation.php b/includes/core/class-validation.php index b835b009..dbdbd564 100644 --- a/includes/core/class-validation.php +++ b/includes/core/class-validation.php @@ -90,13 +90,16 @@ if ( ! class_exists( 'um\core\Validation' ) ) { // Dynamic dropdown options population $has_custom_source = apply_filters("um_has_dropdown_options_source__{$key}", false ); - if ( in_array( $fields[ $key ]['type'], array( 'select','multiselect' ) ) && $has_custom_source ){ - $arr_options = apply_filters("um_get_field__{$key}", $fields[ $key ]['options'] ); - $fields[ $key ]['options'] = array_keys( $arr_options['options'] ); + if ( in_array( $fields[ $key ]['type'], array( 'select','multiselect' ), true ) && $has_custom_source ) { + /** This filter is documented in includes/core/class-fields.php */ + $fields[ $key ] = apply_filters( "um_get_field__{$key}", $fields[ $key ] ); + if ( is_array( $fields[ $key ] ) && array_key_exists( 'options', $fields[ $key ] ) ) { + $fields[ $key ]['options'] = array_keys( $fields[ $key ]['options'] ); + } } // Dropdown options source from callback function - if ( in_array( $fields[ $key ]['type'], array( 'select','multiselect' ) ) && + if ( in_array( $fields[ $key ]['type'], array( 'select','multiselect' ), true ) && isset( $fields[ $key ]['custom_dropdown_options_source'] ) && ! empty( $fields[ $key ]['custom_dropdown_options_source'] ) && function_exists( $fields[ $key ]['custom_dropdown_options_source'] ) ) { diff --git a/includes/core/um-filters-account.php b/includes/core/um-filters-account.php index d7c6613b..ebd103f5 100644 --- a/includes/core/um-filters-account.php +++ b/includes/core/um-filters-account.php @@ -1,29 +1,30 @@ -options()->get( 'account_name_disable' ) ) { return $fields; } if ( um_is_core_page( 'account' ) ) { - $fields['disabled'] = 'disabled="disabled"'; + $fields['disabled'] = ' disabled="disabled" '; } return $fields; } -add_filter( 'um_get_field__first_name', 'um_account_disable_name_fields', 10 ,1 ); -add_filter( 'um_get_field__last_name', 'um_account_disable_name_fields', 10 ,1 ); - +add_filter( 'um_get_field__first_name', 'um_account_disable_name_fields' ); +add_filter( 'um_get_field__last_name', 'um_account_disable_name_fields' ); /** - * Sanitize inputs on Account update + * Sanitize inputs on Account update. * * @param $data * @@ -32,12 +33,15 @@ add_filter( 'um_get_field__last_name', 'um_account_disable_name_fields', 10 ,1 ) function um_account_sanitize_data( $data ) { foreach ( $data as $key => $value ) { if ( is_array( $value ) ) { - $data[ $key ] = array_filter( $value, function( $var ) { - $var = trim( esc_html( strip_tags( $var ) ) ); - return $var; - }); + $data[ $key ] = array_filter( + $value, + function( $var ) { + $var = trim( esc_html( wp_strip_all_tags( $var ) ) ); + return $var; + } + ); } else { - $data[ $key ] = trim( esc_html( strip_tags( $value ) ) ); + $data[ $key ] = trim( esc_html( wp_strip_all_tags( $value ) ) ); } } @@ -45,11 +49,11 @@ function um_account_sanitize_data( $data ) { } add_filter( 'um_account_pre_updating_profile_array', 'um_account_sanitize_data', 10, 1 ); - /** - * Fix for the account field "Avoid indexing my profile by search engines" + * Fix for the account field "Avoid indexing my profile by search engines". + * * @since 2.1.16 - * @param bool $value + * @param mixed|int $value * @return int */ function um_account_profile_noindex_value( $value ) { @@ -58,4 +62,4 @@ function um_account_profile_noindex_value( $value ) { } return $value; } -add_filter( 'um_profile_profile_noindex_empty__filter', 'um_account_profile_noindex_value' ); \ No newline at end of file +add_filter( 'um_profile_profile_noindex_empty__filter', 'um_account_profile_noindex_value' ); From efa1a8a6367fbe20f43734e7768bde19a2d2e39d Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Tue, 4 Jul 2023 13:01:04 +0300 Subject: [PATCH 027/101] - fixed editable field; --- includes/core/class-fields.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-fields.php b/includes/core/class-fields.php index d1e8825e..31d473fb 100644 --- a/includes/core/class-fields.php +++ b/includes/core/class-fields.php @@ -2182,7 +2182,7 @@ if ( ! class_exists( 'um\core\Fields' ) ) { if ( ! empty( $this->editing ) && 'profile' === $this->set_mode ) { if ( ! UM()->roles()->um_user_can( 'can_edit_everyone' ) ) { - if ( isset( $data['editable'] ) && 0 === $data['editable'] ) { + if ( empty( $data['editable'] ) ) { $disabled = ' disabled="disabled" '; } } From 1cb85f376c727431fecf97942bdabcdb9e5dac8b Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Tue, 4 Jul 2023 15:02:49 +0300 Subject: [PATCH 028/101] - fixed custom sorting field's titles; --- templates/members.php | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/templates/members.php b/templates/members.php index 98b4521a..b254d9f0 100644 --- a/templates/members.php +++ b/templates/members.php @@ -6,7 +6,7 @@ * * Page: "Members" * - * @version 2.6.3 + * @version 2.6.8 * * @var array $args */ @@ -64,11 +64,11 @@ if ( count( $args['view_types'] ) == 1 ) { // Sorting $default_sorting = ! empty( $args['sortby'] ) ? $args['sortby'] : 'user_registered_desc'; -if ( $default_sorting == 'other' && ! empty( $args['sortby_custom'] ) ) { +if ( 'other' === $default_sorting && ! empty( $args['sortby_custom'] ) ) { $default_sorting = $args['sortby_custom']; } -$sort_from_url = ''; +$sort_from_url = ''; $custom_sorting_titles = array(); if ( ! empty( $args['enable_sorting'] ) ) { $sorting_options = empty( $args['sorting_fields'] ) ? array() : $args['sorting_fields']; @@ -77,10 +77,10 @@ if ( ! empty( $args['enable_sorting'] ) ) { if ( ! empty( $sorting_options ) ) { foreach ( $sorting_options as $option ) { if ( is_array( $option ) ) { - $option_keys = array_keys( $option ); + $option_keys = array_keys( $option ); $sorting_options_prepared[] = $option_keys[0]; - $custom_sorting_titles[ $option_keys[0] ] = $option[ $option_keys[0] ]; + $custom_sorting_titles[ $option_keys[0] ] = ! empty( $option['label'] ) ? $option['label'] : $option[ $option_keys[0] ]; } else { $sorting_options_prepared[] = $option; } @@ -89,17 +89,17 @@ if ( ! empty( $args['enable_sorting'] ) ) { $all_sorting_options = UM()->member_directory()->sort_fields; - if ( ! in_array( $default_sorting, $sorting_options_prepared ) ) { + if ( ! in_array( $default_sorting, $sorting_options_prepared, true ) ) { $sorting_options_prepared[] = $default_sorting; $label = $default_sorting; - if ( ! empty( $args['sortby_custom_label'] ) && 'other' == $args['sortby'] ) { + if ( ! empty( $args['sortby_custom_label'] ) && 'other' === $args['sortby'] ) { $label = $args['sortby_custom_label']; } elseif ( ! empty( $all_sorting_options[ $default_sorting ] ) ) { $label = $all_sorting_options[ $default_sorting ]; } - $label = ( $label == 'random' ) ? __( 'Random', 'ultimate-member' ) : $label; + $label = ( 'random' === $label ) ? __( 'Random', 'ultimate-member' ) : $label; $custom_sorting_titles[ $default_sorting ] = $label; } @@ -109,32 +109,34 @@ if ( ! empty( $args['enable_sorting'] ) ) { } $sorting_options = apply_filters( 'um_member_directory_pre_display_sorting', $sorting_options, $args ); - $sort_from_url = ( ! empty( $_GET[ 'sort_' . $unique_hash ] ) && in_array( sanitize_text_field( $_GET[ 'sort_' . $unique_hash ] ), array_keys( $sorting_options ) ) ) ? sanitize_text_field( $_GET[ 'sort_' . $unique_hash ] ) : $default_sorting; + $sort_from_url = ( ! empty( $_GET[ 'sort_' . $unique_hash ] ) && in_array( sanitize_text_field( $_GET[ 'sort_' . $unique_hash ] ), array_keys( $sorting_options ) ) ) ? sanitize_text_field( $_GET[ 'sort_' . $unique_hash ] ) : $default_sorting; } $current_page = ( ! empty( $_GET[ 'page_' . $unique_hash ] ) && is_numeric( $_GET[ 'page_' . $unique_hash ] ) ) ? absint( $_GET[ 'page_' . $unique_hash ] ) : 1; //Search -$search = isset( $args['search'] ) ? $args['search'] : false; +$search = isset( $args['search'] ) ? $args['search'] : false; $show_search = empty( $args['roles_can_search'] ) || ( ! empty( $priority_user_role ) && in_array( $priority_user_role, $args['roles_can_search'] ) ); $search_from_url = ''; if ( $search && $show_search ) { $search_from_url = ! empty( $_GET[ 'search_' . $unique_hash ] ) ? stripslashes( sanitize_text_field( $_GET[ 'search_' . $unique_hash ] ) ) : ''; } - //Filters -$filters = isset( $args['filters'] ) ? $args['filters'] : false; -$show_filters = empty( $args['roles_can_filter'] ) || ( ! empty( $priority_user_role ) && in_array( $priority_user_role, $args['roles_can_filter'] ) ); +$filters = isset( $args['filters'] ) ? $args['filters'] : false; +$show_filters = empty( $args['roles_can_filter'] ) || ( ! empty( $priority_user_role ) && in_array( $priority_user_role, $args['roles_can_filter'] ) ); $search_filters = array(); if ( isset( $args['search_fields'] ) ) { $search_filters = apply_filters( 'um_frontend_member_search_filters', array_unique( array_filter( $args['search_fields'] ) ) ); } if ( ! empty( $search_filters ) ) { - $search_filters = array_filter( $search_filters, function( $item ) { - return in_array( $item, array_keys( UM()->member_directory()->filter_fields ) ); - }); + $search_filters = array_filter( + $search_filters, + function( $item ) { + return array_key_exists( $item, UM()->member_directory()->filter_fields ); + } + ); $search_filters = array_values( $search_filters ); } From 8ac7016a1d835f30dffb0a812ba50d58d2f288bc Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Tue, 4 Jul 2023 16:47:32 +0300 Subject: [PATCH 029/101] - added additional hook attributes to 'um_reset_password_errors_hook' and 'um_reset_password_process_hook'; --- includes/core/class-password.php | 60 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/includes/core/class-password.php b/includes/core/class-password.php index a839458e..1b3e5ee4 100644 --- a/includes/core/class-password.php +++ b/includes/core/class-password.php @@ -271,6 +271,10 @@ if ( ! class_exists( 'um\core\Password' ) ) { } if ( $this->is_reset_request() ) { + $form_data = array( + 'mode' => 'password', + ); + UM()->form()->post_form = wp_unslash( $_POST ); if ( empty( UM()->form()->post_form['mode'] ) ) { @@ -278,47 +282,43 @@ if ( ! class_exists( 'um\core\Password' ) ) { } /** - * UM hook + * Fires for handle validate errors on the reset password form submit. * - * @type action - * @title um_reset_password_errors_hook - * @description Action on reset password submit form - * @input_vars - * [{"var":"$post","type":"array","desc":"Form submitted"}] - * @change_log - * ["Since: 2.0"] - * @usage add_action( 'um_reset_password_errors_hook', 'function_name', 10, 1 ); - * @example - * Make any custom validation on password reset form. + * function my_reset_password_errors( $submission_data, $form_data ) { * // your code here * } - * ?> + * add_action( 'um_reset_password_errors_hook', 'my_reset_password_errors', 10, 2 ); */ - do_action( 'um_reset_password_errors_hook', UM()->form()->post_form ); + do_action( 'um_reset_password_errors_hook', UM()->form()->post_form, $form_data ); if ( ! isset( UM()->form()->errors ) ) { /** - * UM hook + * Fires for handle the reset password form when submitted data is valid. * - * @type action - * @title um_reset_password_process_hook - * @description Action on reset password success submit form - * @input_vars - * [{"var":"$post","type":"array","desc":"Form submitted"}] - * @change_log - * ["Since: 2.0"] - * @usage add_action( 'um_reset_password_process_hook', 'function_name', 10, 1 ); - * @example - * Make any custom action when password reset form is submitted. + * function my_reset_password_process( $submission_data, $form_data ) { * // your code here * } - * ?> + * add_action( 'um_reset_password_process_hook', 'my_reset_password_process', 10, 2 ); */ - do_action( 'um_reset_password_process_hook', UM()->form()->post_form ); + do_action( 'um_reset_password_process_hook', UM()->form()->post_form, $form_data ); } } From a3cdba07cb6ef3f43e19f8b6bfd436a9bce73567 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Wed, 5 Jul 2023 12:00:26 +0800 Subject: [PATCH 030/101] Add secure's restore account --- includes/core/class-secure.php | 99 +++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 344b2937..17fe9d3a 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -1,6 +1,9 @@ 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; + + } + } /** @@ -378,19 +416,18 @@ if ( ! class_exists( 'um\core\Secure' ) ) { global $wpdb; // Fetch the WP_User object of our user. um_fetch_user( $user_id ); - $user = new \WP_User( $user_id ); + $user = new WP_User( $user_id ); $has_admin_cap = false; $arr_banned_caps = array(); if ( UM()->options()->get( 'banned_capabilities' ) ) { $arr_banned_caps = array_keys( UM()->options()->get( 'banned_capabilities' ) ); - } else { - $arr_banned_caps = $this->banned_admin_capabilities; } // Add locked administratrive capabilities $arr_banned_caps[] = 'manage_options'; $arr_banned_caps[] = 'promote_users'; + $arr_banned_caps[] = 'level_10'; foreach ( $arr_banned_caps as $i => $cap ) { /** @@ -459,16 +496,18 @@ if ( ! class_exists( 'um\core\Secure' ) ) { require_once um_path . 'includes/lib/browser.php'; } - // Detect browser + // Detect browser. $browser = new \Browser(); - + // Capture details. $captured = array( - 'capabilities' => $user->allcaps, - 'submitted' => UM()->form()->post_form, - 'roles' => $user->roles, - 'user_browser' => $browser, + '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 ), ); update_user_meta( $user->get( 'ID' ), 'um_user_blocked__metadata', $captured ); + $user->remove_all_caps(); if ( is_user_logged_in() ) { UM()->user()->set_status( 'inactive' ); @@ -492,14 +531,20 @@ if ( ! class_exists( 'um\core\Secure' ) ) { */ public function manage_users_custom_column( $val, $column_name, $user_id ) { if ( 'account_status' === $column_name ) { - $is_blocked = get_user_meta( $user_id, 'um_user_blocked', true ); - if ( ! empty( $is_blocked ) ) { - $datetime = get_user_meta( $user_id, 'um_user_blocked__datetime', true ); - $val = $val . '
' . __( 'Blocked Due to Suspicious Activity', 'ultimate-member' ) . '
'; + 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 . '
' . __( 'Blocked Due to Suspicious Activity', 'ultimate-member' ) . '
'; + $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 = ' · ' . __( 'Restore Account', 'ultimate-member' ) . ''; if ( ! empty( $datetime ) ) { - $val = $val . '
' . human_time_diff( strtotime( $datetime ), strtotime( current_time( 'mysql' ) ) ) . ' ' . __( 'ago', 'ultimate-member' ) . '
'; + $val = $val . '
' . human_time_diff( strtotime( $datetime ), strtotime( current_time( 'mysql' ) ) ) . ' ' . __( 'ago', 'ultimate-member' ) . '' . $action . '
'; } } + um_reset_user(); } return $val; } @@ -547,7 +592,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { ), ); - $users = new \WP_User_Query( $args ); + $users = new WP_User_Query( $args ); $this->send_email( array_values( $users->get_results() ) ); } @@ -583,7 +628,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { ), ); - $users = new \WP_User_Query( $args ); + $users = new WP_User_Query( $args ); $this->send_email( array_values( $users->get_results() ) ); @@ -666,7 +711,21 @@ if ( ! class_exists( 'um\core\Secure' ) ) { } + public function admin_notice() { + if ( get_transient( 'um_secure_restore_account_notice_success' ) ) { ?> +
+

+ +

+
+ Date: Wed, 5 Jul 2023 12:51:48 +0800 Subject: [PATCH 031/101] Add secure global notice for first-time --- includes/core/class-secure.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 17fe9d3a..d519503d 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -203,6 +203,10 @@ if ( ! class_exists( 'um\core\Secure' ) ) { } + if ( isset( $_REQUEST['um_dismiss_security_first_time_notice'] ) ) { + set_transient( 'um_secure_first_time_admin_notice', 1, 5 ); + } + } /** @@ -327,7 +331,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { 'options' => $banned_admin_capabilities_options, 'value' => UM()->options()->get( 'banned_capabilities' ) ? array_keys( UM()->options()->get( 'banned_capabilities' ) ) : array_keys( $banned_admin_capabilities_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 Profile & Register form submission, it will be flagged with this option. The manage_options, promote_users & level_10 capabilities are locked to ensure no users will be created with these 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 manage_options, promote_users & level_10 capabilities are locked to ensure no users will be created with these capabilities.', 'ultimate-member' ), ), array( 'id' => 'secure_notify_admins_banned_accounts', @@ -724,6 +728,24 @@ if ( ! class_exists( 'um\core\Secure' ) ) { // 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'] ) ) { + ?> +
+

+
+ +

+

+ + +

+
+ Date: Wed, 5 Jul 2023 12:54:04 +0800 Subject: [PATCH 032/101] Fix admin notice expiration --- includes/core/class-secure.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index d519503d..d621575e 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -204,7 +204,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { } if ( isset( $_REQUEST['um_dismiss_security_first_time_notice'] ) ) { - set_transient( 'um_secure_first_time_admin_notice', 1, 5 ); + set_transient( 'um_secure_first_time_admin_notice', 1, 0 ); } } From 51a5b5ed66ebefdf14f93c0c5adfa58d741ea24e Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Wed, 5 Jul 2023 12:57:42 +0800 Subject: [PATCH 033/101] Add secure doc article link --- includes/core/class-secure.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index d621575e..437e6de0 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -740,7 +740,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) {

- +

Date: Wed, 5 Jul 2023 13:04:08 +0800 Subject: [PATCH 034/101] Fix default locked capabilities --- includes/core/class-secure.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 437e6de0..16093100 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -291,8 +291,9 @@ if ( ! class_exists( 'um\core\Secure' ) ) { $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 ) { - if ( in_array( $cap, array( 'manage_options', 'promote_users', 'level_10' ), true ) ) { + if ( in_array( $cap, $default_locked_cap_options, true ) ) { continue; } $banned_admin_capabilities_options[ $cap ] = $cap; @@ -329,7 +330,7 @@ if ( ! class_exists( 'um\core\Secure' ) ) { 'multi' => true, 'columns' => 2, 'options' => $banned_admin_capabilities_options, - 'value' => UM()->options()->get( 'banned_capabilities' ) ? array_keys( UM()->options()->get( 'banned_capabilities' ) ) : array_keys( $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 manage_options, promote_users & level_10 capabilities are locked to ensure no users will be created with these capabilities.', 'ultimate-member' ), ), From a0b52f26989e53876de21df31aa38e3c94c47b82 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Wed, 5 Jul 2023 13:13:33 +0800 Subject: [PATCH 035/101] Add disabled options in multi checkbox render --- includes/admin/core/class-admin-forms.php | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/includes/admin/core/class-admin-forms.php b/includes/admin/core/class-admin-forms.php index 92355732..e91efd3b 100644 --- a/includes/admin/core/class-admin-forms.php +++ b/includes/admin/core/class-admin-forms.php @@ -1204,19 +1204,20 @@ 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 .= ''; + $per_page = ceil( count( $field_data['options'] ) / $columns ); + $section_fields_per_page = array_slice( $field_data['options'], $i * $per_page, $per_page, true ); + $html .= ''; foreach ( $section_fields_per_page as $k => $title ) { - $id_attr = ' id="' . esc_attr( $id . '_' . $k ) . '" '; - $for_attr = ' for="' . esc_attr( $id . '_' . $k ) . '" '; - $name_attr = ' name="' . $name . '[' . $k . ']" '; + $id_attr = ' id="' . esc_attr( $id . '_' . $k ) . '" '; + $for_attr = ' for="' . esc_attr( $id . '_' . $k ) . '" '; + $name_attr = ' name="' . $name . '[' . $k . ']" '; + $disabed_attr = ''; $data = array( 'field_id' => $field_data['id'] . '_' . $k, @@ -1234,8 +1235,13 @@ if ( ! class_exists( 'um\admin\core\Admin_Forms' ) ) { $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"'; + $values = array_merge( $values, $field_data['options_disabled'] ); + } + $html .= ""; } From 110f22c92e1070c45888a21f3a68445130689773 Mon Sep 17 00:00:00 2001 From: Champ Camba Date: Wed, 5 Jul 2023 13:16:38 +0800 Subject: [PATCH 036/101] Add locked capabilities in the settings to improve readability --- includes/core/class-secure.php | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/includes/core/class-secure.php b/includes/core/class-secure.php index 16093100..3b860170 100644 --- a/includes/core/class-secure.php +++ b/includes/core/class-secure.php @@ -88,8 +88,6 @@ if ( ! class_exists( 'um\core\Secure' ) ) { 'export', 'import', 'list_users', - 'manage_options', - 'promote_users', 'remove_users', 'switch_themes', 'customize', @@ -109,6 +107,8 @@ if ( ! class_exists( 'um\core\Secure' ) ) { 'create_users', 'delete_users', 'level_10', + 'manage_options', + 'promote_users', ) ); @@ -293,9 +293,6 @@ if ( ! class_exists( 'um\core\Secure' ) ) { $banned_admin_capabilities_options = array(); $default_locked_cap_options = array( 'manage_options', 'promote_users', 'level_10' ); foreach ( $this->banned_admin_capabilities as $i => $cap ) { - if ( in_array( $cap, $default_locked_cap_options, true ) ) { - continue; - } $banned_admin_capabilities_options[ $cap ] = $cap; } @@ -325,14 +322,15 @@ if ( ! class_exists( 'um\core\Secure' ) ) { 'description' => __( 'This will logout all users on your site and forces them to reset passwords
when "Display Login form notice to reset passwords" is enabled/checked.', 'ultimate-member' ), ), array( - 'id' => 'banned_capabilities', - 'type' => 'multi_checkbox', - 'multi' => true, - 'columns' => 2, - '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 manage_options, promote_users & level_10 capabilities are locked to ensure no users will be created with these capabilities.', 'ultimate-member' ), + '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 manage_options, promote_users & level_10 capabilities are locked to ensure no users will be created with these capabilities.', 'ultimate-member' ), ), array( 'id' => 'secure_notify_admins_banned_accounts', From 25aa40b1c290c82d879aeedf207af4d430bbbee5 Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Thu, 6 Jul 2023 01:56:59 +0300 Subject: [PATCH 037/101] - review for secure functionality; --- includes/admin/class-admin.php | 12 + includes/admin/core/class-admin-forms.php | 19 +- includes/admin/core/class-admin-notices.php | 77 ++- includes/admin/core/class-admin-settings.php | 15 + includes/admin/core/class-secure.php | 348 ++++++++++++ includes/class-config.php | 12 +- includes/class-functions.php | 3 + includes/class-init.php | 1 + includes/core/class-options.php | 5 +- includes/core/class-secure.php | 531 +++++-------------- 10 files changed, 581 insertions(+), 442 deletions(-) create mode 100644 includes/admin/core/class-secure.php diff --git a/includes/admin/class-admin.php b/includes/admin/class-admin.php index d9131416..8684b5ae 100644 --- a/includes/admin/class-admin.php +++ b/includes/admin/class-admin.php @@ -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']; + } } } diff --git a/includes/admin/core/class-admin-forms.php b/includes/admin/core/class-admin-forms.php index e91efd3b..4b1a7b51 100644 --- a/includes/admin/core/class-admin-forms.php +++ b/includes/admin/core/class-admin-forms.php @@ -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 .= "