Make WordPress Core

Changeset 41254


Ignore:
Timestamp:
08/14/2017 08:12:23 PM (9 years ago)
Author:
johnbillion
Message:

Options, Meta APIs: Require a confirmation link in an email to be clicked when an admin attempts to change the site admin email address.

This adds this previously Multisite-only functionality to single site installations too. This change prevents accidental or erroneous email address changes from potentially locking users out of their site.

Props MatheusGimenez, johnbillion

Fixes #39118

Location:
trunk/src/wp-admin
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/admin-filters.php

    r41164 r41254  
    5656add_action( 'update_option_page_on_front', 'update_home_siteurl', 10, 2 );
    5757add_action( 'update_option_admin_email',   'wp_site_admin_email_change_notification', 10, 3 );
     58
     59add_action( 'add_option_new_admin_email',    'update_option_new_admin_email', 10, 2 );
     60add_action( 'update_option_new_admin_email', 'update_option_new_admin_email', 10, 2 );
    5861
    5962add_filter( 'heartbeat_received', 'wp_check_locked_posts',  10,  3 );
  • trunk/src/wp-admin/includes/misc.php

    r38893 r41254  
    937937        <?php
    938938}
     939
     940/**
     941 * Send a confirmation request email when a change of site admin email address is attempted.
     942 *
     943 * The new site admin address will not become active until confirmed.
     944 *
     945 * @since 3.0.0
     946 * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific.
     947 *
     948 * @param string $old_value The old site admin email address.
     949 * @param string $value     The proposed new site admin email address.
     950 */
     951function update_option_new_admin_email( $old_value, $value ) {
     952        if ( $value == get_option( 'admin_email' ) || ! is_email( $value ) ) {
     953                return;
     954        }
     955
     956        $hash = md5( $value . time() . mt_rand() );
     957        $new_admin_email = array(
     958                'hash'     => $hash,
     959                'newemail' => $value,
     960        );
     961        update_option( 'adminhash', $new_admin_email );
     962
     963        $switched_locale = switch_to_locale( get_user_locale() );
     964
     965        /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
     966        $email_text = __( 'Howdy ###USERNAME###,
     967
     968You recently requested to have the administration email address on
     969your site changed.
     970
     971If this is correct, please click on the following link to change it:
     972###ADMIN_URL###
     973
     974You can safely ignore and delete this email if you do not want to
     975take this action.
     976
     977This email has been sent to ###EMAIL###
     978
     979Regards,
     980All at ###SITENAME###
     981###SITEURL###' );
     982
     983        /**
     984         * Filters the text of the email sent when a change of site admin email address is attempted.
     985         *
     986         * The following strings have a special meaning and will get replaced dynamically:
     987         * ###USERNAME###  The current user's username.
     988         * ###ADMIN_URL### The link to click on to confirm the email change.
     989         * ###EMAIL###     The proposed new site admin email address.
     990         * ###SITENAME###  The name of the site.
     991         * ###SITEURL###   The URL to the site.
     992         *
     993         * @since MU (3.0.0)
     994         * @since 4.9.0 This filter is no longer Multisite specific.
     995         *
     996         * @param string $email_text      Text in the email.
     997         * @param array  $new_admin_email {
     998         *     Data relating to the new site admin email address.
     999         *
     1000         *     @type string $hash     The secure hash used in the confirmation link URL.
     1001         *     @type string $newemail The proposed new site admin email address.
     1002         * }
     1003         */
     1004        $content = apply_filters( 'new_admin_email_content', $email_text, $new_admin_email );
     1005
     1006        $current_user = wp_get_current_user();
     1007        $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
     1008        $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'options.php?adminhash=' . $hash ) ), $content );
     1009        $content = str_replace( '###EMAIL###', $value, $content );
     1010        $content = str_replace( '###SITENAME###', wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), $content );
     1011        $content = str_replace( '###SITEURL###', home_url(), $content );
     1012
     1013        wp_mail( $value, sprintf( __( '[%s] New Admin Email Address' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ), $content );
     1014
     1015        if ( $switched_locale ) {
     1016                restore_previous_locale();
     1017        }
     1018}
  • trunk/src/wp-admin/includes/ms-admin-filters.php

    r41253 r41254  
    1616
    1717add_action( 'admin_page_access_denied', '_access_denied_splash', 99 );
    18 
    19 add_action( 'add_option_new_admin_email', 'update_option_new_admin_email', 10, 2 );
    20 
    21 add_action( 'update_option_new_admin_email', 'update_option_new_admin_email', 10, 2 );
    2218
    2319// Site Hooks.
  • trunk/src/wp-admin/includes/ms.php

    r41242 r41254  
    264264
    265265        return true;
    266 }
    267 
    268 /**
    269  * Send a confirmation request email when a change of site admin email address is attempted.
    270  *
    271  * The new site admin address will not become active until confirmed.
    272  *
    273  * @since 3.0.0
    274  *
    275  * @param string $old_value The old site admin email address.
    276  * @param string $value     The proposed new site admin email address.
    277  */
    278 function update_option_new_admin_email( $old_value, $value ) {
    279         if ( $value == get_option( 'admin_email' ) || !is_email( $value ) )
    280                 return;
    281 
    282         $hash = md5( $value. time() .mt_rand() );
    283         $new_admin_email = array(
    284                 'hash' => $hash,
    285                 'newemail' => $value
    286         );
    287         update_option( 'adminhash', $new_admin_email );
    288 
    289         $switched_locale = switch_to_locale( get_user_locale() );
    290 
    291         /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
    292         $email_text = __( 'Howdy ###USERNAME###,
    293 
    294 You recently requested to have the administration email address on
    295 your site changed.
    296 
    297 If this is correct, please click on the following link to change it:
    298 ###ADMIN_URL###
    299 
    300 You can safely ignore and delete this email if you do not want to
    301 take this action.
    302 
    303 This email has been sent to ###EMAIL###
    304 
    305 Regards,
    306 All at ###SITENAME###
    307 ###SITEURL###' );
    308 
    309         /**
    310          * Filters the text of the email sent when a change of site admin email address is attempted.
    311          *
    312          * The following strings have a special meaning and will get replaced dynamically:
    313          * ###USERNAME###  The current user's username.
    314          * ###ADMIN_URL### The link to click on to confirm the email change.
    315          * ###EMAIL###     The proposed new site admin email address.
    316          * ###SITENAME###  The name of the site.
    317          * ###SITEURL###   The URL to the site.
    318          *
    319          * @since MU (3.0.0)
    320          *
    321          * @param string $email_text      Text in the email.
    322          * @param array  $new_admin_email {
    323          *     Data relating to the new site admin email address.
    324          *
    325          *     @type string $hash     The secure hash used in the confirmation link URL.
    326          *     @type string $newemail The proposed new site admin email address.
    327          * }
    328          */
    329         $content = apply_filters( 'new_admin_email_content', $email_text, $new_admin_email );
    330 
    331         $current_user = wp_get_current_user();
    332         $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
    333         $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'options.php?adminhash='.$hash ) ), $content );
    334         $content = str_replace( '###EMAIL###', $value, $content );
    335         $content = str_replace( '###SITENAME###', wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ), $content );
    336         $content = str_replace( '###SITEURL###', network_home_url(), $content );
    337 
    338         wp_mail( $value, sprintf( __( '[%s] New Admin Email Address' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) ), $content );
    339 
    340         if ( $switched_locale ) {
    341                 restore_previous_locale();
    342         }
    343266}
    344267
  • trunk/src/wp-admin/options-general.php

    r40823 r41254  
    5757
    5858<table class="form-table">
     59
    5960<tr>
    6061<th scope="row"><label for="blogname"><?php _e('Site Title') ?></label></th>
    6162<td><input name="blogname" type="text" id="blogname" value="<?php form_option('blogname'); ?>" class="regular-text" /></td>
    6263</tr>
     64
    6365<tr>
    6466<th scope="row"><label for="blogdescription"><?php _e('Tagline') ?></label></th>
     
    6668<p class="description" id="tagline-description"><?php _e( 'In a few words, explain what this site is about.' ) ?></p></td>
    6769</tr>
     70
    6871<?php if ( !is_multisite() ) { ?>
     72
    6973<tr>
    7074<th scope="row"><label for="siteurl"><?php _e('WordPress Address (URL)') ?></label></th>
    7175<td><input name="siteurl" type="url" id="siteurl" value="<?php form_option( 'siteurl' ); ?>"<?php disabled( defined( 'WP_SITEURL' ) ); ?> class="regular-text code<?php if ( defined( 'WP_SITEURL' ) ) echo ' disabled' ?>" /></td>
    7276</tr>
     77
    7378<tr>
    7479<th scope="row"><label for="home"><?php _e('Site Address (URL)') ?></label></th>
     
    7883<?php endif; ?>
    7984</tr>
    80 <tr>
    81 <th scope="row"><label for="admin_email"><?php _e('Email Address') ?> </label></th>
    82 <td><input name="admin_email" type="email" id="admin_email" aria-describedby="admin-email-description" value="<?php form_option( 'admin_email' ); ?>" class="regular-text ltr" />
    83 <p class="description" id="admin-email-description"><?php _e( 'This address is used for admin purposes, like new user notification.' ) ?></p></td>
    84 </tr>
     85
     86<?php } ?>
     87
     88<tr>
     89<th scope="row"><label for="new_admin_email"><?php _e( 'Email Address' ); ?></label></th>
     90<td><input name="new_admin_email" type="email" id="new_admin_email" aria-describedby="new-admin-email-description" value="<?php form_option( 'admin_email' ); ?>" class="regular-text ltr" />
     91<p class="description" id="new-admin-email-description"><?php _e( 'This address is used for admin purposes. If you change this we will send you an email at your new address to confirm it. <strong>The new address will not become active until confirmed.</strong>' ); ?></p>
     92<?php
     93$new_admin_email = get_option( 'new_admin_email' );
     94if ( $new_admin_email && $new_admin_email != get_option( 'admin_email' ) ) : ?>
     95        <div class="updated inline">
     96        <p><?php
     97                printf(
     98                        /* translators: %s: new admin email */
     99                        __( 'There is a pending change of the admin email to %s.' ),
     100                        '<code>' . esc_html( $new_admin_email ) . '</code>'
     101                );
     102                printf(
     103                        ' <a href="%1$s">%2$s</a>',
     104                        esc_url( wp_nonce_url( admin_url( 'options.php?dismiss=new_admin_email' ), 'dismiss-' . get_current_blog_id() . '-new_admin_email' ) ),
     105                        __( 'Cancel' )
     106                );
     107        ?></p>
     108        </div>
     109<?php endif; ?>
     110</td>
     111</tr>
     112
     113<?php if ( ! is_multisite() ) { ?>
     114
    85115<tr>
    86116<th scope="row"><?php _e('Membership') ?></th>
     
    90120</fieldset></td>
    91121</tr>
     122
    92123<tr>
    93124<th scope="row"><label for="default_role"><?php _e('New User Default Role') ?></label></th>
     
    96127</td>
    97128</tr>
    98 <?php } else { ?>
    99 <tr>
    100 <th scope="row"><label for="new_admin_email"><?php _e('Email Address') ?> </label></th>
    101 <td><input name="new_admin_email" type="email" id="new_admin_email" aria-describedby="new-admin-email-description" value="<?php form_option( 'admin_email' ); ?>" class="regular-text ltr" />
    102 <p class="description" id="new-admin-email-description"><?php _e( 'This address is used for admin purposes. If you change this we will send you an email at your new address to confirm it. <strong>The new address will not become active until confirmed.</strong>' ) ?></p>
    103 <?php
    104 $new_admin_email = get_option( 'new_admin_email' );
    105 if ( $new_admin_email && $new_admin_email != get_option('admin_email') ) : ?>
    106 <div class="updated inline">
    107 <p><?php
    108         printf(
    109                 /* translators: %s: new admin email */
    110                 __( 'There is a pending change of the admin email to %s.' ),
    111                 '<code>' . esc_html( $new_admin_email ) . '</code>'
    112         );
    113         printf(
    114                 ' <a href="%1$s">%2$s</a>',
    115                 esc_url( wp_nonce_url( admin_url( 'options.php?dismiss=new_admin_email' ), 'dismiss-' . get_current_blog_id() . '-new_admin_email' ) ),
    116                 __( 'Cancel' )
    117         );
    118 ?></p>
    119 </div>
    120 <?php endif; ?>
    121 </td>
    122 </tr>
     129
    123130<?php }
    124131
  • trunk/src/wp-admin/options.php

    r39933 r41254  
    5454
    5555// Handle admin email change requests
    56 if ( is_multisite() ) {
    57         if ( ! empty($_GET[ 'adminhash' ] ) ) {
    58                 $new_admin_details = get_option( 'adminhash' );
    59                 $redirect = 'options-general.php?updated=false';
    60                 if ( is_array( $new_admin_details ) && hash_equals( $new_admin_details[ 'hash' ], $_GET[ 'adminhash' ] ) && !empty($new_admin_details[ 'newemail' ]) ) {
    61                         update_option( 'admin_email', $new_admin_details[ 'newemail' ] );
    62                         delete_option( 'adminhash' );
    63                         delete_option( 'new_admin_email' );
    64                         $redirect = 'options-general.php?updated=true';
    65                 }
    66                 wp_redirect( admin_url( $redirect ) );
    67                 exit;
    68         } elseif ( ! empty( $_GET['dismiss'] ) && 'new_admin_email' == $_GET['dismiss'] ) {
    69                 check_admin_referer( 'dismiss-' . get_current_blog_id() . '-new_admin_email' );
     56if ( ! empty( $_GET[ 'adminhash' ] ) ) {
     57        $new_admin_details = get_option( 'adminhash' );
     58        $redirect = 'options-general.php?updated=false';
     59        if ( is_array( $new_admin_details ) && hash_equals( $new_admin_details[ 'hash' ], $_GET[ 'adminhash' ] ) && ! empty( $new_admin_details[ 'newemail' ] ) ) {
     60                update_option( 'admin_email', $new_admin_details[ 'newemail' ] );
    7061                delete_option( 'adminhash' );
    7162                delete_option( 'new_admin_email' );
    72                 wp_redirect( admin_url( 'options-general.php?updated=true' ) );
    73                 exit;
    74         }
     63                $redirect = 'options-general.php?updated=true';
     64        }
     65        wp_redirect( admin_url( $redirect ) );
     66        exit;
     67} elseif ( ! empty( $_GET['dismiss'] ) && 'new_admin_email' == $_GET['dismiss'] ) {
     68        check_admin_referer( 'dismiss-' . get_current_blog_id() . '-new_admin_email' );
     69        delete_option( 'adminhash' );
     70        delete_option( 'new_admin_email' );
     71        wp_redirect( admin_url( 'options-general.php?updated=true' ) );
     72        exit;
    7573}
    7674
     
    8482
    8583$whitelist_options = array(
    86         'general' => array( 'blogname', 'blogdescription', 'gmt_offset', 'date_format', 'time_format', 'start_of_week', 'timezone_string', 'WPLANG' ),
     84        'general' => array( 'blogname', 'blogdescription', 'gmt_offset', 'date_format', 'time_format', 'start_of_week', 'timezone_string', 'WPLANG', 'new_admin_email' ),
    8785        'discussion' => array( 'default_pingback_flag', 'default_ping_status', 'default_comment_status', 'comments_notify', 'moderation_notify', 'comment_moderation', 'require_name_email', 'comment_whitelist', 'comment_max_links', 'moderation_keys', 'blacklist_keys', 'show_avatars', 'avatar_rating', 'avatar_default', 'close_comments_for_old_posts', 'close_comments_days_old', 'thread_comments', 'thread_comments_depth', 'page_comments', 'comments_per_page', 'default_comments_page', 'comment_order', 'comment_registration' ),
    8886        'media' => array( 'thumbnail_size_w', 'thumbnail_size_h', 'thumbnail_crop', 'medium_size_w', 'medium_size_h', 'large_size_w', 'large_size_h', 'image_default_size', 'image_default_align', 'image_default_link_type' ),
     
    108106                $whitelist_options['general'][] = 'home';
    109107
    110         $whitelist_options['general'][] = 'admin_email';
    111108        $whitelist_options['general'][] = 'users_can_register';
    112109        $whitelist_options['general'][] = 'default_role';
     
    123120        }
    124121} else {
    125         $whitelist_options['general'][] = 'new_admin_email';
    126 
    127122        /**
    128123         * Filters whether the post-by-email functionality is enabled.
Note: See TracChangeset for help on using the changeset viewer.