Introduce a button on the user profile screen which clears all other sessions, and on the user editing screen which clears all sessions. Only appears when there are applicable sessions which can be cleared.

See #30264.
Props jorbin, ocean90, johnbillion


Built from https://develop.svn.wordpress.org/trunk@30333


git-svn-id: http://core.svn.wordpress.org/trunk@30332 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
John Blackbourn 2014-11-13 15:21:21 +00:00
parent f93a197089
commit c02845330e
13 changed files with 142 additions and 12 deletions

View File

@ -61,7 +61,7 @@ $core_actions_post = array(
'query-attachments', 'save-attachment', 'save-attachment-compat', 'send-link-to-editor',
'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs',
'save-user-color-scheme', 'update-widget', 'query-themes', 'parse-embed', 'set-attachment-thumbnail',
'parse-media-shortcode'
'parse-media-shortcode', 'destroy-sessions'
);
// Register core Ajax calls.

View File

@ -611,6 +611,11 @@ table.form-table td .updated {
font-size: 13px;
}
table.form-table td .updated p {
font-size: 13px;
margin: 0.3em 0;
}
/*------------------------------------------------------------------------------
18.0 - Users
------------------------------------------------------------------------------*/

View File

@ -611,6 +611,11 @@ table.form-table td .updated {
font-size: 13px;
}
table.form-table td .updated p {
font-size: 13px;
margin: 0.3em 0;
}
/*------------------------------------------------------------------------------
18.0 - Users
------------------------------------------------------------------------------*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2770,3 +2770,54 @@ function wp_ajax_parse_media_shortcode() {
'body' => ob_get_clean()
) );
}
/**
* AJAX handler for destroying multiple open sessions for a user.
*
* @since 4.1.0
*
*/
function wp_ajax_destroy_sessions() {
if ( empty( $_POST['user_id'] ) ) {
$user = new WP_Error();
} else {
$user = new WP_User( absint( $_POST['user_id'] ) );
if ( ! $user->exists() ) {
$user = new WP_Error();
} elseif ( ! current_user_can( 'edit_user', $user->ID ) ) {
$user = new WP_Error();
} elseif ( ! check_ajax_referer( sprintf( 'destroy_sessions_%d', $user->ID ), false, false ) ) {
$user = new WP_Error();
}
}
if ( is_wp_error( $user ) ) {
wp_send_json_error( array(
'message' => __( 'Could not log out user sessions. Please try again.' ),
) );
}
if ( isset( $_POST['token'] ) ) {
$keep = wp_unslash( $_POST['token'] );
} else {
$keep = null;
}
$sessions = WP_Session_Tokens::get_instance( $user->ID );
if ( is_string( $keep ) ) {
$sessions->destroy_others( $keep );
$message = __( 'You are now logged out everywhere else' );
} else {
$sessions->destroy_all();
/* translators: 1: User's display name. */
$message = sprintf( __( '%s has been logged out' ), $user->display_name );
}
wp_send_json_success( array(
'message' => $message
) );
}

View File

@ -1,4 +1,4 @@
/* global ajaxurl, pwsL10n */
/* global ajaxurl, pwsL10n, _wpSessionMangager */
(function($){
function check_pass_strength() {
@ -124,4 +124,29 @@
});
});
$('#destroy-sessions').on('click',function(e){
var $this = $(this);
var data = {
action : 'destroy-sessions',
_ajax_nonce : _wpSessionMangager.nonce,
user_id : _wpSessionMangager.user_id,
token : $(this).data('token')
};
$.post( ajaxurl, data, function( response ) {
if ( response.success ) {
$this.prop( 'disabled', true );
$this.before( '<div class="updated inline"><p>' + response.data.message + '</p></div>' );
} else {
$this.before( '<div class="error inline"><p>' + response.data.message + '</p></div>' );
}
}, 'json' );
e.preventDefault();
});
})(jQuery);

View File

@ -1 +1 @@
!function(a){function b(){var b,c=a("#pass1").val(),d=a("#pass2").val();if(a("#pass-strength-result").removeClass("short bad good strong"),!c)return void a("#pass-strength-result").html(pwsL10n.empty);switch(b=wp.passwordStrength.meter(c,wp.passwordStrength.userInputBlacklist(),d)){case 2:a("#pass-strength-result").addClass("bad").html(pwsL10n.bad);break;case 3:a("#pass-strength-result").addClass("good").html(pwsL10n.good);break;case 4:a("#pass-strength-result").addClass("strong").html(pwsL10n.strong);break;case 5:a("#pass-strength-result").addClass("short").html(pwsL10n.mismatch);break;default:a("#pass-strength-result").addClass("short").html(pwsL10n["short"])}}a(document).ready(function(){var c,d,e,f,g=a("#display_name");a("#pass1").val("").keyup(b),a("#pass2").val("").keyup(b),a("#pass-strength-result").show(),a(".color-palette").click(function(){a(this).siblings('input[name="admin_color"]').prop("checked",!0)}),g.length&&a("#first_name, #last_name, #nickname").bind("blur.user_profile",function(){var b=[],c={display_nickname:a("#nickname").val()||"",display_username:a("#user_login").val()||"",display_firstname:a("#first_name").val()||"",display_lastname:a("#last_name").val()||""};c.display_firstname&&c.display_lastname&&(c.display_firstlast=c.display_firstname+" "+c.display_lastname,c.display_lastfirst=c.display_lastname+" "+c.display_firstname),a.each(a("option",g),function(a,c){b.push(c.value)}),a.each(c,function(d,e){if(e){var f=e.replace(/<\/?[a-z][^>]*>/gi,"");c[d].length&&-1===a.inArray(f,b)&&(b.push(f),a("<option />",{text:f}).appendTo(g))}})}),c=a("#color-picker"),d=a("#colors-css"),e=a("input#user_id").val(),f=a('input[name="checkuser_id"]').val(),c.on("click.colorpicker",".color-option",function(){var b,c=a(this);if(!c.hasClass("selected")&&(c.siblings(".selected").removeClass("selected"),c.addClass("selected").find('input[type="radio"]').prop("checked",!0),e===f)){if(0===d.length&&(d=a('<link rel="stylesheet" />').appendTo("head")),d.attr("href",c.children(".css_url").val()),"undefined"!=typeof wp&&wp.svgPainter){try{b=a.parseJSON(c.children(".icon_colors").val())}catch(g){}b&&(wp.svgPainter.setColors(b),wp.svgPainter.paint())}a.post(ajaxurl,{action:"save-user-color-scheme",color_scheme:c.children('input[name="admin_color"]').val(),nonce:a("#color-nonce").val()})}})})}(jQuery);
!function(a){function b(){var b,c=a("#pass1").val(),d=a("#pass2").val();if(a("#pass-strength-result").removeClass("short bad good strong"),!c)return void a("#pass-strength-result").html(pwsL10n.empty);switch(b=wp.passwordStrength.meter(c,wp.passwordStrength.userInputBlacklist(),d)){case 2:a("#pass-strength-result").addClass("bad").html(pwsL10n.bad);break;case 3:a("#pass-strength-result").addClass("good").html(pwsL10n.good);break;case 4:a("#pass-strength-result").addClass("strong").html(pwsL10n.strong);break;case 5:a("#pass-strength-result").addClass("short").html(pwsL10n.mismatch);break;default:a("#pass-strength-result").addClass("short").html(pwsL10n["short"])}}a(document).ready(function(){var c,d,e,f,g=a("#display_name");a("#pass1").val("").keyup(b),a("#pass2").val("").keyup(b),a("#pass-strength-result").show(),a(".color-palette").click(function(){a(this).siblings('input[name="admin_color"]').prop("checked",!0)}),g.length&&a("#first_name, #last_name, #nickname").bind("blur.user_profile",function(){var b=[],c={display_nickname:a("#nickname").val()||"",display_username:a("#user_login").val()||"",display_firstname:a("#first_name").val()||"",display_lastname:a("#last_name").val()||""};c.display_firstname&&c.display_lastname&&(c.display_firstlast=c.display_firstname+" "+c.display_lastname,c.display_lastfirst=c.display_lastname+" "+c.display_firstname),a.each(a("option",g),function(a,c){b.push(c.value)}),a.each(c,function(d,e){if(e){var f=e.replace(/<\/?[a-z][^>]*>/gi,"");c[d].length&&-1===a.inArray(f,b)&&(b.push(f),a("<option />",{text:f}).appendTo(g))}})}),c=a("#color-picker"),d=a("#colors-css"),e=a("input#user_id").val(),f=a('input[name="checkuser_id"]').val(),c.on("click.colorpicker",".color-option",function(){var b,c=a(this);if(!c.hasClass("selected")&&(c.siblings(".selected").removeClass("selected"),c.addClass("selected").find('input[type="radio"]').prop("checked",!0),e===f)){if(0===d.length&&(d=a('<link rel="stylesheet" />').appendTo("head")),d.attr("href",c.children(".css_url").val()),"undefined"!=typeof wp&&wp.svgPainter){try{b=a.parseJSON(c.children(".icon_colors").val())}catch(g){}b&&(wp.svgPainter.setColors(b),wp.svgPainter.paint())}a.post(ajaxurl,{action:"save-user-color-scheme",color_scheme:c.children('input[name="admin_color"]').val(),nonce:a("#color-nonce").val()})}})}),a("#destroy-sessions").on("click",function(b){var c=a(this),d={action:"destroy-sessions",_ajax_nonce:_wpSessionMangager.nonce,user_id:_wpSessionMangager.user_id,token:a(this).data("token")};a.post(ajaxurl,d,function(a){a.success?(c.prop("disabled",!0),c.before('<div class="updated inline"><p>'+a.data.message+"</p></div>")):c.before('<div class="error inline"><p>'+a.data.message+"</p></div>")},"json"),b.preventDefault()})}(jQuery);

View File

@ -25,6 +25,15 @@ elseif ( ! get_userdata( $user_id ) )
wp_enqueue_script('user-profile');
wp_localize_script(
'user-profile',
'_wpSessionMangager',
array(
'user_id' => $user_id,
'nonce' => wp_create_nonce( sprintf( 'destroy_sessions_%d', $user_id ) ),
)
);
$title = IS_PROFILE_PAGE ? __('Profile') : __('Edit User');
if ( current_user_can('edit_users') && !IS_PROFILE_PAGE )
$submenu_file = 'users.php';
@ -187,6 +196,8 @@ $profileuser = get_user_to_edit($user_id);
if ( !current_user_can('edit_user', $user_id) )
wp_die(__('You do not have permission to edit this user.'));
$sessions = WP_Session_Tokens::get_instance( $profileuser->ID );
include(ABSPATH . 'wp-admin/admin-header.php');
?>
@ -289,6 +300,7 @@ if ( !( IS_PROFILE_PAGE && !$user_can_edit ) ) : ?>
*/
do_action( 'personal_options', $profileuser );
?>
</table>
<?php
if ( IS_PROFILE_PAGE ) {
@ -474,6 +486,29 @@ if ( $show_password_fields ) :
</td>
</tr>
<?php endif; ?>
<?php if ( IS_PROFILE_PAGE && ( count( $sessions->get_all() ) > 1 ) ) { ?>
<tr>
<th>&nbsp;</th>
<td>
<p><button class="button button-secondary hide-if-no-js" id="destroy-sessions" data-token="<?php echo esc_attr( wp_get_session_token() ); ?>"><?php _e( 'Log Out of All Other Sessions' ); ?></button></p>
<p class="description hide-if-no-js">
<?php _e( 'Left your account logged in at a public computer? Lost your phone? This will log you out everywhere except your current browser.' ); ?>
</p>
</td>
</tr>
<?php } else if ( ! IS_PROFILE_PAGE && ( count( $sessions->get_all() ) > 0 ) ) { ?>
<tr>
<th>&nbsp;</th>
<td>
<p><button class="button button-secondary hide-if-no-js" id="destroy-sessions"><?php _e( 'Log Out of All Sessions' ); ?></button></p>
<p class="description hide-if-no-js">
<?php printf( __( 'Log %s out of all sessions' ), $profileuser->display_name ); ?>
</p>
</td>
</tr>
<?php } ?>
</table>
<?php

View File

@ -131,6 +131,19 @@ abstract class WP_Session_Tokens {
$session = apply_filters( 'attach_session_information', array(), $this->user_id );
$session['expiration'] = $expiration;
// IP address.
if ( !empty( $_SERVER['REMOTE_ADDR'] ) ) {
$session['ip'] = $_SERVER['REMOTE_ADDR'];
}
// User-agent.
if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
$session['ua'] = wp_unslash( $_SERVER['HTTP_USER_AGENT'] );
}
// Timestamp
$session['login'] = time();
$token = wp_generate_password( 43, false, false );
$this->update( $token, $session );
@ -383,10 +396,6 @@ class WP_User_Meta_Session_Tokens extends WP_Session_Tokens {
* @param array $sessions Sessions.
*/
protected function update_sessions( $sessions ) {
if ( ! has_filter( 'attach_session_information' ) ) {
$sessions = wp_list_pluck( $sessions, 'expiration' );
}
if ( $sessions ) {
update_user_meta( $this->user_id, 'session_tokens', $sessions );
} else {

View File

@ -4,7 +4,7 @@
*
* @global string $wp_version
*/
$wp_version = '4.1-alpha-30332';
$wp_version = '4.1-alpha-30333';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.