diff --git wp-includes/user.php wp-includes/user.php
index def16fa272..561e8b2131 100644
|
|
function new_user_email_admin_notice() { |
2815 | 2815 | * |
2816 | 2816 | * @since 5.0.0 |
2817 | 2817 | * |
2818 | | * @param string $action_name Name of the action that is being confirmed. |
2819 | | * @param string $action_description User facing description of the action they will be confirming. |
2820 | | * @param string $email User email address. This can be the address of a registered or non-registered user. Defaults to logged in user email address. |
2821 | | * |
2822 | | * @return WP_ERROR|bool Will return true/false based on the success of sending the email, or a WP_Error object. |
| 2818 | * @param string $email User email address. This can be the address of a registered or non-registered user. Defaults to logged in user email address. |
| 2819 | * @param string $action_name Name of the action that is being confirmed. Defaults to 'confirm_email'. |
| 2820 | * @param string $action_description User facing description of the action they will be confirming. Defaults to "confirm your email address". |
| 2821 | * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. |
| 2822 | * @return WP_Error|bool Will return true/false based on the success of sending the email, or a WP_Error object. |
2823 | 2823 | */ |
2824 | | function send_confirm_account_action_email( $action_name, $action_description = '', $email = '' ) { |
| 2824 | function wp_send_account_verification_key( $email = '', $action_name = '', $action_description = '', $request_data = array() ) { |
| 2825 | if ( ! function_exists( 'wp_get_current_user' ) ) { |
| 2826 | return new WP_Error( 'invalid', __( 'This function cannot be used before init.' ) ); |
| 2827 | } |
| 2828 | |
2825 | 2829 | $action_name = sanitize_key( $action_name ); |
2826 | 2830 | $action_description = wp_kses_post( $action_description ); |
2827 | 2831 | |
2828 | 2832 | if ( empty( $action_name ) ) { |
2829 | | return new WP_Error( 'invalid_action', __( 'Invalid action' ) ); |
| 2833 | $action_name = 'confirm_email'; |
| 2834 | } |
| 2835 | |
| 2836 | if ( empty( $action_description ) ) { |
| 2837 | $action_description = __( 'Confirm your email address.' ); |
2830 | 2838 | } |
2831 | 2839 | |
2832 | 2840 | if ( empty( $email ) ) { |
… |
… |
function send_confirm_account_action_email( $action_name, $action_description = |
2846 | 2854 | $user = get_user_by( 'email', $email ); |
2847 | 2855 | } |
2848 | 2856 | |
2849 | | // We could be dealing with a registered user account, or a visitor. |
2850 | | $is_registered_user = $user && ! is_wp_error( $user ); |
2851 | | $uid = $is_registered_user ? $user->ID : hash( 'sha256', $email ); |
2852 | | $confirm_key = get_confirm_account_action_key( $action_name, $email ); |
| 2857 | $confirm_key = wp_get_account_verification_key( $email, $action_name, $request_data ); |
2853 | 2858 | |
2854 | 2859 | if ( is_wp_error( $confirm_key ) ) { |
2855 | 2860 | return $confirm_key; |
2856 | 2861 | } |
2857 | 2862 | |
2858 | | // Prepare the email content. |
2859 | | if ( ! $action_description ) { |
2860 | | $action_description = $action_name; |
| 2863 | // We could be dealing with a registered user account, or a visitor. |
| 2864 | $is_registered_user = $user && ! is_wp_error( $user ); |
| 2865 | |
| 2866 | if ( $is_registered_user ) { |
| 2867 | $uid = $user->ID; |
| 2868 | } else { |
| 2869 | // Generate a UID for this email address so we don't send the actual email in the query string. Hash is not supported on all systems. |
| 2870 | $uid = function_exists( 'hash' ) ? hash( 'sha256', $email ) : sha1( $email ); |
2861 | 2871 | } |
2862 | 2872 | |
2863 | 2873 | /* translators: Do not translate DESCRIPTION, CONFIRM_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ |
2864 | 2874 | $email_text = __( |
2865 | 2875 | 'Howdy, |
2866 | 2876 | |
2867 | | An account linked to your email address has requested to perform |
2868 | | the following action: |
| 2877 | A request has been made to perform the following action on your account: |
2869 | 2878 | |
2870 | 2879 | ###DESCRIPTION### |
2871 | 2880 | |
2872 | | To confirm this action, please click on the following link: |
| 2881 | To confirm this, please click on the following link: |
2873 | 2882 | ###CONFIRM_URL### |
2874 | 2883 | |
2875 | 2884 | You can safely ignore and delete this email if you do not want to |
… |
… |
All at ###SITENAME### |
2887 | 2896 | 'email' => $email, |
2888 | 2897 | 'description' => $action_description, |
2889 | 2898 | 'confirm_url' => add_query_arg( array( |
2890 | | 'action' => 'emailconfirm', |
| 2899 | 'action' => 'verifyaccount', |
2891 | 2900 | 'confirm_action' => $action_name, |
2892 | 2901 | 'uid' => $uid, |
2893 | 2902 | 'confirm_key' => $confirm_key, |
… |
… |
All at ###SITENAME### |
2907 | 2916 | * ###SITENAME### The name of the site. |
2908 | 2917 | * ###SITEURL### The URL to the site. |
2909 | 2918 | * |
| 2919 | * @since 5.0.0 |
| 2920 | * |
2910 | 2921 | * @param string $email_text Text in the email. |
2911 | 2922 | * @param array $email_data { |
2912 | 2923 | * Data relating to the account action email. |
… |
… |
All at ###SITENAME### |
2919 | 2930 | * @type string $siteurl The site URL sending the mail. |
2920 | 2931 | * } |
2921 | 2932 | */ |
2922 | | $content = apply_filters( 'confirm_account_action_email_content', $email_text, $email_data ); |
| 2933 | $content = apply_filters( 'account_verification_email_content', $email_text, $email_data ); |
2923 | 2934 | |
2924 | 2935 | $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content ); |
2925 | 2936 | $content = str_replace( '###CONFIRM_URL###', esc_url_raw( $email_data['confirm_url'] ), $content ); |
… |
… |
All at ###SITENAME### |
2928 | 2939 | $content = str_replace( '###SITEURL###', esc_url_raw( $email_data['siteurl'] ), $content ); |
2929 | 2940 | |
2930 | 2941 | /* translators: %s Site name. */ |
2931 | | return wp_mail( $email_data['email'], sprintf( __( '[%s] Confirm Account Action' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ), $content ); |
| 2942 | return wp_mail( $email_data['email'], sprintf( __( '[%s] Confirm Action' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ), $content ); |
2932 | 2943 | } |
2933 | 2944 | |
2934 | 2945 | /** |
… |
… |
All at ###SITENAME### |
2936 | 2947 | * |
2937 | 2948 | * @since 5.0.0 |
2938 | 2949 | * |
2939 | | * @param string $action_name Name of the action this key is being generated for. |
2940 | | * @param string $email User email address. This can be the address of a registered or non-registered user. |
2941 | | * |
| 2950 | * @param string $email User email address. This can be the address of a registered or non-registered user. |
| 2951 | * @param string $action_name Name of the action this key is being generated for. |
| 2952 | * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. |
2942 | 2953 | * @return string|WP_Error Confirmation key on success. WP_Error on error. |
2943 | 2954 | */ |
2944 | | function get_confirm_account_action_key( $action_name, $email ) { |
| 2955 | function wp_get_account_verification_key( $email, $action_name, $request_data = array() ) { |
2945 | 2956 | global $wp_hasher; |
2946 | 2957 | |
2947 | 2958 | if ( ! is_email( $email ) ) { |
… |
… |
function get_confirm_account_action_key( $action_name, $email ) { |
2967 | 2978 | } |
2968 | 2979 | |
2969 | 2980 | $hashed_key = $wp_hasher->HashPassword( $key ); |
| 2981 | $value = array( |
| 2982 | 'time' => time(), |
| 2983 | 'hash' => $hashed_key, |
| 2984 | 'email' => $email, |
| 2985 | 'request_data' => $request_data, |
| 2986 | ); |
2970 | 2987 | |
2971 | 2988 | if ( $is_registered_user ) { |
2972 | | $key_saved = (bool) update_user_meta( $user->ID, '_account_action_' . $action_name, implode( ':', array( time(), $hashed_key ) ) ); |
| 2989 | $key_saved = (bool) update_user_meta( $user->ID, '_verify_' . $action_name, wp_json_encode( $value ) ); |
2973 | 2990 | } else { |
2974 | | $key_saved = (bool) update_site_option( '_account_action_' . hash( 'sha256', $email ) . '_' . $action_name, implode( ':', array( time(), $hashed_key, $email ) ) ); |
| 2991 | $uid = function_exists( 'hash' ) ? hash( 'sha256', $email ) : sha1( $email ); |
| 2992 | $key_saved = (bool) update_site_option( '_verify_' . $action_name . '_' . $uid, wp_json_encode( $value ) ); |
2975 | 2993 | } |
2976 | 2994 | |
2977 | 2995 | if ( false === $key_saved ) { |
2978 | | return new WP_Error( 'no_confirm_account_action_key_update', __( 'Could not save confirm account action key to database.' ) ); |
| 2996 | return new WP_Error( 'no_account_verification_key_update', __( 'Could not save confirm account action key to database.' ) ); |
2979 | 2997 | } |
2980 | 2998 | |
2981 | 2999 | return $key; |
… |
… |
function get_confirm_account_action_key( $action_name, $email ) { |
2986 | 3004 | * |
2987 | 3005 | * @since 5.0.0 |
2988 | 3006 | * |
2989 | | * @param string $action_name Name of the action this key is being generated for. |
2990 | 3007 | * @param string $key Key to confirm. |
2991 | 3008 | * @param string $uid Email hash or user ID. |
2992 | | * |
| 3009 | * @param string $action_name Name of the action this key is being generated for. |
2993 | 3010 | * @return array|WP_Error WP_Error on failure, action name and user email address on success. |
2994 | 3011 | */ |
2995 | | function check_confirm_account_action_key( $action_name, $key, $uid ) { |
| 3012 | function wp_check_account_verification_key( $key, $uid, $action_name ) { |
2996 | 3013 | global $wp_hasher; |
2997 | 3014 | |
2998 | | if ( ! empty( $action_name ) && ! empty( $key ) && ! empty( $uid ) ) { |
2999 | | $user = false; |
| 3015 | if ( empty( $action_name ) || empty( $key ) || empty( $uid ) ) { |
| 3016 | return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); |
| 3017 | } |
3000 | 3018 | |
3001 | | if ( is_numeric( $uid ) ) { |
3002 | | $user = get_user_by( 'id', absint( $uid ) ); |
3003 | | } |
| 3019 | $user = false; |
3004 | 3020 | |
3005 | | // We could be dealing with a registered user account, or a visitor. |
3006 | | $is_registered_user = $user && ! is_wp_error( $user ); |
3007 | | $key_request_time = ''; |
3008 | | $saved_key = ''; |
3009 | | $email = ''; |
| 3021 | if ( is_numeric( $uid ) ) { |
| 3022 | $user = get_user_by( 'id', absint( $uid ) ); |
| 3023 | } |
3010 | 3024 | |
3011 | | if ( empty( $wp_hasher ) ) { |
3012 | | require_once ABSPATH . WPINC . '/class-phpass.php'; |
3013 | | $wp_hasher = new PasswordHash( 8, true ); |
3014 | | } |
| 3025 | // We could be dealing with a registered user account, or a visitor. |
| 3026 | $is_registered_user = ( $user && ! is_wp_error( $user ) ); |
| 3027 | $key_request_time = ''; |
| 3028 | $saved_key = ''; |
| 3029 | $email = ''; |
3015 | 3030 | |
3016 | | // Get the saved key from the database. |
3017 | | if ( $is_registered_user ) { |
3018 | | $confirm_action_data = get_user_meta( $user->ID, '_account_action_' . $action_name, true ); |
3019 | | $email = $user->user_email; |
| 3031 | if ( empty( $wp_hasher ) ) { |
| 3032 | require_once ABSPATH . WPINC . '/class-phpass.php'; |
| 3033 | $wp_hasher = new PasswordHash( 8, true ); |
| 3034 | } |
3020 | 3035 | |
3021 | | if ( false !== strpos( $confirm_action_data, ':' ) ) { |
3022 | | list( $key_request_time, $saved_key ) = explode( ':', $confirm_action_data, 2 ); |
3023 | | } |
3024 | | } else { |
3025 | | $confirm_action_data = get_site_option( '_account_action_' . $uid . '_' . $action_name, '' ); |
| 3036 | // Get the saved key from the database. |
| 3037 | if ( $is_registered_user ) { |
| 3038 | $raw_data = get_user_meta( $user->ID, '_verify_' . $action_name, true ); |
| 3039 | $email = $user->user_email; |
3026 | 3040 | |
3027 | | if ( false !== strpos( $confirm_action_data, ':' ) ) { |
3028 | | list( $key_request_time, $saved_key, $email ) = explode( ':', $confirm_action_data, 3 ); |
3029 | | } |
| 3041 | if ( false !== strpos( $confirm_action_data, ':' ) ) { |
| 3042 | list( $key_request_time, $saved_key ) = explode( ':', $confirm_action_data, 2 ); |
3030 | 3043 | } |
| 3044 | } else { |
| 3045 | $raw_data = get_site_option( '_verify_' . $action_name . '_' . $uid, '' ); |
3031 | 3046 | |
3032 | | if ( ! $saved_key ) { |
3033 | | return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); |
| 3047 | if ( false !== strpos( $confirm_action_data, ':' ) ) { |
| 3048 | list( $key_request_time, $saved_key, $email ) = explode( ':', $confirm_action_data, 3 ); |
3034 | 3049 | } |
| 3050 | } |
3035 | 3051 | |
3036 | | /** |
3037 | | * Filters the expiration time of confirm keys. |
3038 | | * |
3039 | | * @param int $expiration The expiration time in seconds. |
3040 | | */ |
3041 | | $expiration_duration = apply_filters( 'account_action_expiration', DAY_IN_SECONDS ); |
3042 | | $expiration_time = $key_request_time + $expiration_duration; |
3043 | | |
3044 | | if ( $wp_hasher->CheckPassword( $key, $saved_key ) ) { |
3045 | | if ( $expiration_time && time() < $expiration_time ) { |
3046 | | $return = array( |
3047 | | 'action' => $action_name, |
3048 | | 'email' => $email, |
3049 | | ); |
3050 | | } else { |
3051 | | $return = new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) ); |
3052 | | } |
| 3052 | $data = json_decode( $raw_data, true ); |
| 3053 | $key_request_time = (int) isset( $data['time'] ) ? $data['time'] : 0; |
| 3054 | $saved_key = isset( $data['hash'] ) ? $data['hash'] : ''; |
| 3055 | $email = sanitize_email( isset( $data['email'] ) ? $data['email'] : '' ); |
| 3056 | $request_data = isset( $data['request_data'] ) ? $data['request_data'] : array(); |
3053 | 3057 | |
3054 | | // Clean up stored keys. |
3055 | | if ( $is_registered_user ) { |
3056 | | delete_user_meta( $user->ID, '_account_action_' . $action_name ); |
3057 | | } else { |
3058 | | delete_site_option( '_account_action_' . $uid . '_' . $action_name ); |
3059 | | } |
| 3058 | if ( ! $saved_key ) { |
| 3059 | return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); |
| 3060 | } |
3060 | 3061 | |
3061 | | return $return; |
3062 | | } |
| 3062 | if ( ! $key_request_time || ! $email ) { |
| 3063 | return new WP_Error( 'invalid_key', __( 'Invalid action' ) ); |
3063 | 3064 | } |
3064 | 3065 | |
3065 | | return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); |
3066 | | } |
| 3066 | /** |
| 3067 | * Filters the expiration time of confirm keys. |
| 3068 | * |
| 3069 | * @since 5.0.0 |
| 3070 | * |
| 3071 | * @param int $expiration The expiration time in seconds. |
| 3072 | */ |
| 3073 | $expiration_duration = apply_filters( 'account_verification_expiration', DAY_IN_SECONDS ); |
| 3074 | $expiration_time = $key_request_time + $expiration_duration; |
| 3075 | |
| 3076 | if ( ! $wp_hasher->CheckPassword( $key, $saved_key ) ) { |
| 3077 | return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); |
| 3078 | } |
| 3079 | |
| 3080 | if ( $expiration_time && time() < $expiration_time ) { |
| 3081 | $return = array( |
| 3082 | 'action' => $action_name, |
| 3083 | 'email' => $email, |
| 3084 | 'request_data' => $request_data, |
| 3085 | ); |
| 3086 | } else { |
| 3087 | $return = new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) ); |
| 3088 | } |
| 3089 | |
| 3090 | // Clean up stored keys. |
| 3091 | if ( $is_registered_user ) { |
| 3092 | delete_user_meta( $user->ID, '_verify_' . $action_name ); |
| 3093 | } else { |
| 3094 | delete_site_option( '_verify_' . $action_name . '_' . $uid ); |
| 3095 | } |
| 3096 | |
| 3097 | return $return; |
| 3098 | } |
| 3099 | No newline at end of file |
diff --git wp-login.php wp-login.php
index 349f232443..815b44fbad 100644
|
|
if ( isset( $_GET['key'] ) ) { |
427 | 427 | } |
428 | 428 | |
429 | 429 | // validate action so as to default to the login screen |
430 | | if ( ! in_array( $action, array( 'postpass', 'logout', 'lostpassword', 'retrievepassword', 'resetpass', 'rp', 'register', 'login', 'emailconfirm' ), true ) && false === has_filter( 'login_form_' . $action ) ) { |
| 430 | if ( ! in_array( $action, array( 'postpass', 'logout', 'lostpassword', 'retrievepassword', 'resetpass', 'rp', 'register', 'login', 'verifyaccount' ), true ) && false === has_filter( 'login_form_' . $action ) ) { |
431 | 431 | $action = 'login'; |
432 | 432 | } |
433 | 433 | |
… |
… |
switch ( $action ) { |
858 | 858 | |
859 | 859 | break; |
860 | 860 | |
861 | | case 'emailconfirm' : |
| 861 | case 'verifyaccount' : |
862 | 862 | if ( isset( $_GET['confirm_action'], $_GET['confirm_key'], $_GET['uid'] ) ) { |
863 | | $action_name = sanitize_key( wp_unslash( $_GET['confirm_action'] ) ); |
864 | 863 | $key = sanitize_text_field( wp_unslash( $_GET['confirm_key'] ) ); |
865 | 864 | $uid = sanitize_text_field( wp_unslash( $_GET['uid'] ) ); |
866 | | $result = check_confirm_account_action_key( $action_name, $key, $uid ); |
| 865 | $action_name = sanitize_key( wp_unslash( $_GET['confirm_action'] ) ); |
| 866 | $result = wp_check_account_verification_key( $key, $uid, $action_name ); |
867 | 867 | } else { |
868 | 868 | $result = new WP_Error( 'invalid_key', __( 'Invalid key' ) ); |
869 | 869 | } |
… |
… |
switch ( $action ) { |
874 | 874 | * |
875 | 875 | * After running this action hook the page will die. |
876 | 876 | * |
| 877 | * @since 5.0.0 |
| 878 | * |
877 | 879 | * @param WP_Error $result Error object. |
878 | 880 | */ |
879 | 881 | do_action( 'account_action_failed', $result ); |
… |
… |
switch ( $action ) { |
887 | 889 | * Using this you can assume the user has agreed to perform the action by |
888 | 890 | * clicking on the link in the confirmation email. |
889 | 891 | * |
890 | | * After firing this action hook the page will redirect to wp-login a callback |
| 892 | * After firing this action hook the page will redirect to wp-login a callback |
891 | 893 | * redirects or exits first. |
892 | 894 | * |
| 895 | * @since 5.0.0 |
| 896 | * |
893 | 897 | * @param array $result { |
894 | 898 | * Data about the action which was confirmed. |
895 | 899 | * |