Changeset 43008 for trunk/src/wp-includes/user.php
- Timestamp:
- 04/27/2018 10:12:01 AM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/user.php
r42967 r43008 2814 2814 * Get all user privacy request types. 2815 2815 * 2816 * @since 5.0.02816 * @since 4.9.6 2817 2817 * @access private 2818 2818 * … … 2821 2821 function _wp_privacy_action_request_types() { 2822 2822 return array( 2823 ' user_export_request',2824 ' user_remove_request',2823 'export_personal_data', 2824 'remove_personal_data', 2825 2825 ); 2826 2826 } … … 2829 2829 * Update log when privacy request is confirmed. 2830 2830 * 2831 * @since 5.0.02831 * @since 4.9.6 2832 2832 * @access private 2833 2833 * 2834 * @param array $result Result of the request from the user. 2835 */ 2836 function _wp_privacy_account_request_confirmed( $result ) { 2837 if ( isset( $result['action'], $result['request_data'], $result['request_data']['privacy_request_id'] ) && in_array( $result['action'], _wp_privacy_action_request_types(), true ) ) { 2838 $privacy_request_id = absint( $result['request_data']['privacy_request_id'] ); 2839 $privacy_request = get_post( $privacy_request_id ); 2840 2841 if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) { 2842 return; 2843 } 2844 2845 update_post_meta( $privacy_request_id, '_confirmed_timestamp', time() ); 2834 * @param int $request_id ID of the request. 2835 */ 2836 function _wp_privacy_account_request_confirmed( $request_id ) { 2837 $request_data = wp_get_user_request_data( $request_id ); 2838 2839 if ( ! $request_data ) { 2840 return; 2841 } 2842 2843 if ( ! in_array( $request_data['status'], array( 'request-pending', 'request-failed' ), true ) ) { 2844 return; 2845 } 2846 2847 update_post_meta( $request_id, '_wp_user_request_confirmed_timestamp', time() ); 2848 wp_update_post( array( 2849 'ID' => $request_data['request_id'], 2850 'post_status' => 'request-confirmed', 2851 ) ); 2852 } 2853 2854 /** 2855 * Return request confirmation message HTML. 2856 * 2857 * @since 4.9.6 2858 * @access private 2859 * 2860 * @return string $message The confirmation message. 2861 */ 2862 function _wp_privacy_account_request_confirmed_message( $message, $request_id ) { 2863 $request = wp_get_user_request_data( $request_id ); 2864 2865 if ( $request && in_array( $request['action'], _wp_privacy_action_request_types(), true ) ) { 2866 $message = '<p class="message">' . __( 'Action has been confirmed.' ) . '</p>'; 2867 $message .= __( 'The site administrator has been notified and will fulfill your request as soon as possible.' ); 2868 } 2869 2870 return $message; 2871 } 2872 2873 /** 2874 * Create and log a user request to perform a specific action. 2875 * 2876 * Requests are stored inside a post type named `user_request` since they can apply to both 2877 * users on the site, or guests without a user account. 2878 * 2879 * @since 4.9.6 2880 * 2881 * @param string $email_address User email address. This can be the address of a registered or non-registered user. 2882 * @param string $action_name Name of the action that is being confirmed. Required. 2883 * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. 2884 * @return int|WP_Error Returns the request ID if successful, or a WP_Error object on failure. 2885 */ 2886 function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array() ) { 2887 $email_address = sanitize_email( $email_address ); 2888 $action_name = sanitize_key( $action_name ); 2889 2890 if ( ! is_email( $email_address ) ) { 2891 return new WP_Error( 'invalid_email', __( 'Invalid email address' ) ); 2892 } 2893 2894 if ( ! $action_name ) { 2895 return new WP_Error( 'invalid_action', __( 'Invalid action name' ) ); 2896 } 2897 2898 $user = get_user_by( 'email', $email_address ); 2899 $user_id = $user && ! is_wp_error( $user ) ? $user->ID: 0; 2900 2901 // Check for duplicates. 2902 $requests_query = new WP_Query( array( 2903 'post_type' => 'user_request', 2904 'title' => $action_name, 2905 'post_status' => 'any', 2906 'fields' => 'ids', 2907 'meta_query' => array( 2908 array( 2909 'key' => '_wp_user_request_user_email', 2910 'value' => $email_address, 2911 ), 2912 ), 2913 ) ); 2914 2915 if ( $requests_query->found_posts ) { 2916 return new WP_Error( 'duplicate_request', __( 'A request for this email address already exists.' ) ); 2917 } 2918 2919 $request_id = wp_insert_post( array( 2920 'post_author' => $user_id, 2921 'post_title' => $action_name, 2922 'post_content' => wp_json_encode( $request_data ), 2923 'post_status' => 'request-pending', 2924 'post_type' => 'user_request', 2925 'post_date' => current_time( 'mysql', false ), 2926 'post_date_gmt' => current_time( 'mysql', true ), 2927 ), true ); 2928 2929 if ( is_wp_error( $request_id ) ) { 2930 return $request_id; 2931 } 2932 2933 update_post_meta( $request_id, '_wp_user_request_user_email', $email_address ); 2934 update_post_meta( $request_id, '_wp_user_request_confirmed_timestamp', false ); 2935 2936 return $request_id; 2937 } 2938 2939 /** 2940 * Get action description from the name and return a string. 2941 * 2942 * @since 4.9.6 2943 * 2944 * @param string $action_name Action name of the request. 2945 * @return string 2946 */ 2947 function wp_user_request_action_description( $action_name ) { 2948 switch ( $action_name ) { 2949 case 'export_personal_data': 2950 $description = __( 'Export Personal Data' ); 2951 break; 2952 case 'remove_personal_data': 2953 $description = __( 'Remove Personal Data' ); 2954 break; 2955 default: 2956 /* translators: %s: action name */ 2957 $description = sprintf( __( 'Confirm the "%s" action' ), $action_name ); 2958 break; 2959 } 2960 2961 /** 2962 * Filters the user action description. 2963 * 2964 * @param string $description The default description. 2965 * @param string $action_name The name of the request. 2966 */ 2967 return apply_filters( 'user_request_action_description', $description, $action_name ); 2968 } 2969 2970 /** 2971 * Send a confirmation request email to confirm an action. 2972 * 2973 * If the request is not already pending, it will be updated. 2974 * 2975 * @since 4.9.6 2976 * 2977 * @param string $request_id ID of the request created via wp_create_user_request(). 2978 * @return WP_Error|bool Will return true/false based on the success of sending the email, or a WP_Error object. 2979 */ 2980 function wp_send_user_request( $request_id ) { 2981 $request_id = absint( $request_id ); 2982 $request = get_post( $request_id ); 2983 2984 if ( ! $request || 'user_request' !== $request->post_type ) { 2985 return new WP_Error( 'user_request_error', __( 'Invalid request.' ) ); 2986 } 2987 2988 if ( 'request-pending' !== $request->post_status ) { 2846 2989 wp_update_post( array( 2847 'ID' => $privacy_request_id, 2848 'post_status' => 'request-confirmed', 2990 'ID' => $request_id, 2991 'post_status' => 'request-pending', 2992 'post_date' => current_time( 'mysql', false ), 2993 'post_date_gmt' => current_time( 'mysql', true ), 2849 2994 ) ); 2850 2995 } 2851 } 2852 add_action( 'account_action_confirmed', '_wp_privacy_account_request_confirmed' ); 2853 2854 /** 2855 * Update log when privacy request fails. 2856 * 2857 * @since 5.0.0 2858 * @access private 2859 * 2860 * @param array $result Result of the request from the user. 2861 */ 2862 function _wp_privacy_account_request_failed( $result ) { 2863 if ( isset( $result['action'], $result['request_data'], $result['request_data']['privacy_request_id'] ) && 2864 in_array( $result['action'], _wp_privacy_action_request_types(), true ) ) { 2865 2866 $privacy_request_id = absint( $result['request_data']['privacy_request_id'] ); 2867 $privacy_request = get_post( $privacy_request_id ); 2868 2869 if ( ! $privacy_request || ! in_array( $privacy_request->post_type, _wp_privacy_action_request_types(), true ) ) { 2870 return; 2871 } 2872 2873 wp_update_post( array( 2874 'ID' => $privacy_request_id, 2875 'post_status' => 'request-failed', 2876 ) ); 2877 } 2878 } 2879 2880 /** 2881 * Send a confirmation request email to confirm an action. 2882 * 2883 * @since 5.0.0 2884 * 2885 * @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. 2886 * @param string $action_name Name of the action that is being confirmed. Defaults to 'confirm_email'. 2887 * @param string $action_description User facing description of the action they will be confirming. Defaults to "confirm your email address". 2888 * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. 2889 * @return WP_Error|bool Will return true/false based on the success of sending the email, or a WP_Error object. 2890 */ 2891 function wp_send_account_verification_key( $email = '', $action_name = '', $action_description = '', $request_data = array() ) { 2892 if ( ! function_exists( 'wp_get_current_user' ) ) { 2893 return new WP_Error( 'invalid', __( 'This function cannot be used before init.' ) ); 2894 } 2895 2896 $action_name = sanitize_key( $action_name ); 2897 $action_description = wp_kses_post( $action_description ); 2898 2899 if ( empty( $action_name ) ) { 2900 $action_name = 'confirm_email'; 2901 } 2902 2903 if ( empty( $action_description ) ) { 2904 $action_description = __( 'Confirm your email address.' ); 2905 } 2906 2907 if ( empty( $email ) ) { 2908 $user = wp_get_current_user(); 2909 $email = $user->ID ? $user->user_email : ''; 2910 } else { 2911 $user = false; 2912 } 2913 2914 $email = sanitize_email( $email ); 2915 2916 if ( ! is_email( $email ) ) { 2917 return new WP_Error( 'invalid_email', __( 'Invalid email address' ) ); 2918 } 2919 2920 if ( ! $user ) { 2921 $user = get_user_by( 'email', $email ); 2922 } 2923 2924 $confirm_key = wp_get_account_verification_key( $email, $action_name, $request_data ); 2925 2926 if ( is_wp_error( $confirm_key ) ) { 2927 return $confirm_key; 2928 } 2929 2930 // We could be dealing with a registered user account, or a visitor. 2931 $is_registered_user = $user && ! is_wp_error( $user ); 2932 2933 if ( $is_registered_user ) { 2934 $uid = $user->ID; 2935 } else { 2936 // 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. 2937 $uid = function_exists( 'hash' ) ? hash( 'sha256', $email ) : sha1( $email ); 2938 } 2996 2997 $email_data = array( 2998 'action_name' => $request->post_title, 2999 'email' => get_post_meta( $request->ID, '_wp_user_request_user_email', true ), 3000 'description' => wp_user_request_action_description( $request->post_title ), 3001 'confirm_url' => add_query_arg( array( 3002 'action' => 'confirmaction', 3003 'request_id' => $request_id, 3004 'confirm_key' => wp_generate_user_request_key( $request_id ), 3005 ), site_url( 'wp-login.php' ) ), 3006 'sitename' => is_multisite() ? get_site_option( 'site_name' ) : get_option( 'blogname' ), 3007 'siteurl' => network_home_url(), 3008 ); 2939 3009 2940 3010 /* translators: Do not translate DESCRIPTION, CONFIRM_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ … … 2957 3027 All at ###SITENAME### 2958 3028 ###SITEURL###' 2959 );2960 2961 $email_data = array(2962 'action_name' => $action_name,2963 'email' => $email,2964 'description' => $action_description,2965 'confirm_url' => add_query_arg( array(2966 'action' => 'verifyaccount',2967 'confirm_action' => $action_name,2968 'uid' => $uid,2969 'confirm_key' => $confirm_key,2970 ), site_url( 'wp-login.php' ) ),2971 'sitename' => is_multisite() ? get_site_option( 'site_name' ) : get_option( 'blogname' ),2972 'siteurl' => network_home_url(),2973 3029 ); 2974 3030 … … 2984 3040 * ###SITEURL### The URL to the site. 2985 3041 * 2986 * @since 5.0.03042 * @since 4.9.6 2987 3043 * 2988 3044 * @param string $email_text Text in the email. … … 2998 3054 * } 2999 3055 */ 3000 $content = apply_filters( ' account_verification_email_content', $email_text, $email_data );3056 $content = apply_filters( 'user_request_action_email_content', $email_text, $email_data ); 3001 3057 3002 3058 $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content ); … … 3011 3067 3012 3068 /** 3013 * Creates, stores, then returns a confirmation key for an account action. 3014 * 3015 * @since 5.0.0 3016 * 3017 * @param string $email User email address. This can be the address of a registered or non-registered user. 3018 * @param string $action_name Name of the action this key is being generated for. 3019 * @param array $request_data Misc data you want to send with the verification request and pass to the actions once the request is confirmed. 3020 * @return string|WP_Error Confirmation key on success. WP_Error on error. 3021 */ 3022 function wp_get_account_verification_key( $email, $action_name, $request_data = array() ) { 3069 * Returns a confirmation key for a user action and stores the hashed version. 3070 * 3071 * @since 4.9.6 3072 * 3073 * @param int $request_id Request ID. 3074 * @return string Confirmation key. 3075 */ 3076 function wp_generate_user_request_key( $request_id ) { 3023 3077 global $wp_hasher; 3024 3025 if ( ! is_email( $email ) ) {3026 return new WP_Error( 'invalid_email', __( 'Invalid email address' ) );3027 }3028 3029 if ( empty( $action_name ) ) {3030 return new WP_Error( 'invalid_action', __( 'Invalid action' ) );3031 }3032 3033 $user = get_user_by( 'email', $email );3034 3035 // We could be dealing with a registered user account, or a visitor.3036 $is_registered_user = $user && ! is_wp_error( $user );3037 3078 3038 3079 // Generate something random for a confirmation key. 3039 3080 $key = wp_generate_password( 20, false ); 3040 3081 3041 // Now insert the key, hashed, into the DB.3082 // Return the key, hashed. 3042 3083 if ( empty( $wp_hasher ) ) { 3043 3084 require_once ABSPATH . WPINC . '/class-phpass.php'; … … 3045 3086 } 3046 3087 3047 $hashed_key = $wp_hasher->HashPassword( $key ); 3048 $value = array( 3049 'action' => $action_name, 3050 'time' => time(), 3051 'hash' => $hashed_key, 3052 'email' => $email, 3053 'request_data' => $request_data, 3054 ); 3055 3056 if ( $is_registered_user ) { 3057 $key_saved = (bool) update_user_meta( $user->ID, '_verify_action_' . $action_name, wp_json_encode( $value ) ); 3058 } else { 3059 $uid = function_exists( 'hash' ) ? hash( 'sha256', $email ) : sha1( $email ); 3060 $key_saved = (bool) update_site_option( '_verify_action_' . $action_name . '_' . $uid, wp_json_encode( $value ) ); 3061 } 3062 3063 if ( false === $key_saved ) { 3064 return new WP_Error( 'no_account_verification_key_update', __( 'Could not save confirm account action key to database.' ) ); 3065 } 3088 update_post_meta( $request_id, '_wp_user_request_confirm_key', $wp_hasher->HashPassword( $key ) ); 3089 update_post_meta( $request_id, '_wp_user_request_confirm_key_timestamp', time() ); 3066 3090 3067 3091 return $key; … … 3069 3093 3070 3094 /** 3071 * Checks if a key is valid and handles the action based on this. 3072 * 3073 * @since 5.0.0 3074 * 3075 * @param string $key Key to confirm. 3076 * @param string $uid Email hash or user ID. 3077 * @param string $action_name Name of the action this key is being generated for. 3078 * @return array|WP_Error WP_Error on failure, action name and user email address on success. 3079 */ 3080 function wp_check_account_verification_key( $key, $uid, $action_name ) { 3095 * Valdate a user request by comparing the key with the request's key. 3096 * 3097 * @since 4.9.6 3098 * 3099 * @param string $request_id ID of the request being confirmed. 3100 * @param string $key Provided key to validate. 3101 * @return bool|WP_Error WP_Error on failure, true on success. 3102 */ 3103 function wp_validate_user_request_key( $request_id, $key ) { 3081 3104 global $wp_hasher; 3082 3105 3083 if ( empty( $action_name ) || empty( $key ) || empty( $uid ) ) { 3106 $request_id = absint( $request_id ); 3107 $request = wp_get_user_request_data( $request_id ); 3108 3109 if ( ! $request ) { 3110 return new WP_Error( 'user_request_error', __( 'Invalid request.' ) ); 3111 } 3112 3113 if ( ! in_array( $request['status'], array( 'request-pending', 'request-failed' ), true ) ) { 3114 return __( 'This link has expired.' ); 3115 } 3116 3117 if ( empty( $key ) ) { 3084 3118 return new WP_Error( 'invalid_key', __( 'Invalid key' ) ); 3085 3119 } 3086 3087 $user = false;3088 3089 if ( is_numeric( $uid ) ) {3090 $user = get_user_by( 'id', absint( $uid ) );3091 }3092 3093 // We could be dealing with a registered user account, or a visitor.3094 $is_registered_user = ( $user && ! is_wp_error( $user ) );3095 $key_request_time = '';3096 $saved_key = '';3097 $email = '';3098 3120 3099 3121 if ( empty( $wp_hasher ) ) { … … 3102 3124 } 3103 3125 3104 // Get the saved key from the database. 3105 if ( $is_registered_user ) { 3106 $raw_data = get_user_meta( $user->ID, '_verify_action_' . $action_name, true ); 3107 $email = $user->user_email; 3108 3109 if ( false !== strpos( $raw_data, ':' ) ) { 3110 list( $key_request_time, $saved_key ) = explode( ':', $raw_data, 2 ); 3111 } 3112 } else { 3113 $raw_data = get_site_option( '_verify_action_' . $action_name . '_' . $uid, '' ); 3114 3115 if ( false !== strpos( $raw_data, ':' ) ) { 3116 list( $key_request_time, $saved_key, $email ) = explode( ':', $raw_data, 3 ); 3117 } 3118 } 3119 3120 $data = json_decode( $raw_data, true ); 3121 $key_request_time = (int) isset( $data['time'] ) ? $data['time'] : 0; 3122 $saved_key = isset( $data['hash'] ) ? $data['hash'] : ''; 3123 $email = sanitize_email( isset( $data['email'] ) ? $data['email'] : '' ); 3124 $request_data = isset( $data['request_data'] ) ? $data['request_data'] : array(); 3126 $key_request_time = $request['confirm_key_timestamp']; 3127 $saved_key = $request['confirm_key']; 3125 3128 3126 3129 if ( ! $saved_key ) { … … 3128 3131 } 3129 3132 3130 if ( ! $key_request_time || ! $email) {3133 if ( ! $key_request_time ) { 3131 3134 return new WP_Error( 'invalid_key', __( 'Invalid action' ) ); 3132 3135 } … … 3135 3138 * Filters the expiration time of confirm keys. 3136 3139 * 3137 * @since 5.0.03140 * @since 4.9.6 3138 3141 * 3139 3142 * @param int $expiration The expiration time in seconds. 3140 3143 */ 3141 $expiration_duration = apply_filters( 'account_verification_expiration', DAY_IN_SECONDS );3144 $expiration_duration = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS ); 3142 3145 $expiration_time = $key_request_time + $expiration_duration; 3143 3146 … … 3146 3149 } 3147 3150 3148 if ( $expiration_time && time() < $expiration_time ) { 3149 $return = array( 3150 'action' => $action_name, 3151 'email' => $email, 3152 'request_data' => $request_data, 3153 ); 3154 } else { 3151 if ( ! $expiration_time || time() > $expiration_time ) { 3155 3152 $return = new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) ); 3156 3153 } 3157 3154 3158 // Clean up stored keys. 3159 if ( $is_registered_user ) { 3160 delete_user_meta( $user->ID, '_verify_action_' . $action_name ); 3161 } else { 3162 delete_site_option( '_verify_action_' . $action_name . '_' . $uid ); 3163 } 3164 3165 return $return; 3166 } 3155 return true; 3156 } 3157 3158 /** 3159 * Return data about a user request. 3160 * 3161 * @since 4.9.6 3162 * 3163 * @param int $request_id Request ID to get data about. 3164 * @return array|false 3165 */ 3166 function wp_get_user_request_data( $request_id ) { 3167 $request_id = absint( $request_id ); 3168 $request = get_post( $request_id ); 3169 3170 if ( ! $request || 'user_request' !== $request->post_type ) { 3171 return false; 3172 } 3173 3174 return array( 3175 'request_id' => $request->ID, 3176 'user_id' => $request->post_author, 3177 'email' => get_post_meta( $request->ID, '_wp_user_request_user_email', true ), 3178 'action' => $request->post_title, 3179 'requested_timestamp' => strtotime( $request->post_date_gmt ), 3180 'confirmed_timestamp' => get_post_meta( $request->ID, '_wp_user_request_confirmed_timestamp', true ), 3181 'completed_timestamp' => get_post_meta( $request->ID, '_wp_user_request_completed_timestamp', true ), 3182 'request_data' => json_decode( $request->post_content, true ), 3183 'status' => $request->post_status, 3184 'confirm_key' => get_post_meta( $request_id, '_wp_user_request_confirm_key', true ), 3185 'confirm_key_timestamp' => get_post_meta( $request_id, '_wp_user_request_confirm_key_timestamp', true ), 3186 ); 3187 }
Note: See TracChangeset
for help on using the changeset viewer.