From a17d508e55440659caefcc31253c2c38ef7ff76a Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 2 Nov 2023 13:54:15 -0400 Subject: [PATCH 1/6] Allow a url to be passed in to load after login --- one-time-login.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/one-time-login.php b/one-time-login.php index 12bf239..30a65e6 100644 --- a/one-time-login.php +++ b/one-time-login.php @@ -18,10 +18,11 @@ * @param WP_User|null $user ID, email address, or user login for the user. * @param int $count Generate a specified number of login tokens (default: 1). * @param bool $delay_delete Delete existing tokens after 15 minutes, instead of immediately. + * @param string $redirect_to Send the user to this wp-admin URL after logging in. * * @return array */ -function one_time_login_generate_tokens( $user, $count, $delay_delete ) { +function one_time_login_generate_tokens( $user, $count, $delay_delete, $redirect_to = '' ) { $tokens = $new_tokens = array(); $login_urls = array(); @@ -46,6 +47,9 @@ function one_time_login_generate_tokens( $user, $count, $delay_delete ) { 'user_id' => $user->ID, 'one_time_login_token' => $token, ); + if ($redirect_to) { + $query_args['redirect_to'] = urlencode($redirect_to); + } $login_urls[] = add_query_arg( $query_args, wp_login_url() ); } } @@ -61,6 +65,7 @@ function one_time_login_generate_tokens( $user, $count, $delay_delete ) { * * [--count=] * [--delay-delete] + * [--redirect-to=] * * ## EXAMPLES * @@ -77,8 +82,9 @@ function one_time_login_wp_cli_command( $args, $assoc_args ) { $user = $fetcher->get_check( $args[0] ); $delay_delete = WP_CLI\Utils\get_flag_value( $assoc_args, 'delay-delete' ); $count = (int) ( $assoc_args['count'] ?? 1 ); + $redirect_to = $assoc_args['redirect-to'] ?? ''; - $login_urls = one_time_login_generate_tokens( $user, $count, $delay_delete ); + $login_urls = one_time_login_generate_tokens( $user, $count, $delay_delete, $redirect_to ); foreach ( $login_urls as $login_url ) { WP_CLI::log( $login_url ); } @@ -244,7 +250,12 @@ function one_time_login_handle_token() { update_user_meta( $user->ID, 'one_time_login_token', $tokens ); wp_set_auth_cookie( $user->ID, true, is_ssl() ); do_action( 'one_time_login_after_auth_cookie_set', $user ); - wp_safe_redirect( admin_url() ); + $admin_url = admin_url(); + if (isset($_GET['redirect_to'])) { + $admin_url .= urldecode($_GET['redirect_to']); + $admin_url = sanitize_url($admin_url); + } + wp_safe_redirect( $admin_url ); exit; } From 858a95248e8e3b49f26c3829afa77ea5e6edf81c Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 2 Nov 2023 13:58:37 -0400 Subject: [PATCH 2/6] fix formatting issues --- one-time-login.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/one-time-login.php b/one-time-login.php index 30a65e6..d411878 100644 --- a/one-time-login.php +++ b/one-time-login.php @@ -43,12 +43,12 @@ function one_time_login_generate_tokens( $user, $count, $delay_delete, $redirect update_user_meta( $user->ID, 'one_time_login_token', $tokens ); do_action( 'one_time_login_created', $user ); foreach ( $new_tokens as $token ) { - $query_args = array( + $query_args = array( 'user_id' => $user->ID, 'one_time_login_token' => $token, ); - if ($redirect_to) { - $query_args['redirect_to'] = urlencode($redirect_to); + if ( $redirect_to ) { + $query_args['redirect_to'] = urlencode( $redirect_to ); } $login_urls[] = add_query_arg( $query_args, wp_login_url() ); } @@ -251,11 +251,11 @@ function one_time_login_handle_token() { wp_set_auth_cookie( $user->ID, true, is_ssl() ); do_action( 'one_time_login_after_auth_cookie_set', $user ); $admin_url = admin_url(); - if (isset($_GET['redirect_to'])) { - $admin_url .= urldecode($_GET['redirect_to']); - $admin_url = sanitize_url($admin_url); + if ( isset( $_GET['redirect_to'] ) ) { + $admin_url .= urldecode( $_GET['redirect_to'] ); + $admin_url = sanitize_url( $admin_url ); } - wp_safe_redirect( $admin_url ); + wp_safe_redirect( $admin_url ); exit; } From 86d02c481b532f417ab667b97f873ed73ad7fecd Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 3 Nov 2023 11:37:26 -0400 Subject: [PATCH 3/6] Store redirect_to target in user_meta --- one-time-login.php | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/one-time-login.php b/one-time-login.php index d411878..39ef721 100644 --- a/one-time-login.php +++ b/one-time-login.php @@ -36,20 +36,20 @@ function one_time_login_generate_tokens( $user, $count, $delay_delete, $redirect for ( $i = 0; $i < $count; $i++ ) { $password = wp_generate_password(); $token = sha1( $password ); - $tokens[] = $token; + $tokens[] = array( + 'token' => $token, + 'redirect_to' => $redirect_to, + ); $new_tokens[] = $token; } update_user_meta( $user->ID, 'one_time_login_token', $tokens ); do_action( 'one_time_login_created', $user ); foreach ( $new_tokens as $token ) { - $query_args = array( + $query_args = array( 'user_id' => $user->ID, 'one_time_login_token' => $token, ); - if ( $redirect_to ) { - $query_args['redirect_to'] = urlencode( $redirect_to ); - } $login_urls[] = add_query_arg( $query_args, wp_login_url() ); } } @@ -95,7 +95,7 @@ function one_time_login_wp_cli_command( $args, $assoc_args ) { } /** - * Generate one-time tokens using WP CLI. + * Generate one-time tokens using WP REST API. * * ## OPTIONS * @@ -114,7 +114,6 @@ function one_time_login_wp_cli_command( $args, $assoc_args ) { * @return WP_REST_Response */ function one_time_login_api_request( WP_REST_Request $request ) { - $user = get_user_by( 'login', $request['user'] ); $delay_delete = (bool) ( $request['delay_delete'] ?? false ); $count = (int) ( $request['count'] ?? 1 ); @@ -157,6 +156,7 @@ function one_time_login_rest_api_init() { return false; } $user = get_user_by( 'login', $request['user'] ); + return current_user_can( 'edit_user', $user->ID ); }, ), @@ -231,13 +231,26 @@ function one_time_login_handle_token() { wp_die( $error ); } - $tokens = get_user_meta( $user->ID, 'one_time_login_token', true ); - $tokens = is_string( $tokens ) ? array( $tokens ) : $tokens; - $is_valid = false; + $tokens = get_user_meta( $user->ID, 'one_time_login_token', true ); + $tokens = is_string( $tokens ) ? array( $tokens ) : $tokens; + $is_valid = false; + $redirect_to = ''; + foreach ( $tokens as $i => $token ) { - if ( hash_equals( $token, $_GET['one_time_login_token'] ) ) { + $this_token = ''; + if ( is_array( $token ) ) { + $this_token = $token['token']; + } else { + $this_token = $token; + } + if ( hash_equals( $this_token, $_GET['one_time_login_token'] ) ) { $is_valid = true; unset( $tokens[ $i ] ); + + if ( is_array( $token ) && isset( $token['redirect_to'] ) ) { + $redirect_to = $token['redirect_to']; + } + break; } } @@ -250,12 +263,7 @@ function one_time_login_handle_token() { update_user_meta( $user->ID, 'one_time_login_token', $tokens ); wp_set_auth_cookie( $user->ID, true, is_ssl() ); do_action( 'one_time_login_after_auth_cookie_set', $user ); - $admin_url = admin_url(); - if ( isset( $_GET['redirect_to'] ) ) { - $admin_url .= urldecode( $_GET['redirect_to'] ); - $admin_url = sanitize_url( $admin_url ); - } - wp_safe_redirect( $admin_url ); + wp_safe_redirect( admin_url() . $redirect_to ); exit; } From dfab99b2f405c61a4f8b932d773003cf27a32f7e Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 5 Dec 2023 10:47:12 -0500 Subject: [PATCH 4/6] phpcs fixes --- one-time-login.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/one-time-login.php b/one-time-login.php index c29d244..6420b11 100644 --- a/one-time-login.php +++ b/one-time-login.php @@ -263,7 +263,7 @@ function one_time_login_handle_token() { update_user_meta( $user->ID, 'one_time_login_token', $tokens ); wp_set_auth_cookie( $user->ID, true, is_ssl() ); do_action( 'one_time_login_after_auth_cookie_set', $user ); - one_time_login_safe_redirect( admin_url() ); + one_time_login_safe_redirect( admin_url() . $redirect_to ); } add_action( 'init', 'one_time_login_handle_token' ); From ccce3bfe79327ba70342f0dee00397a4f3380dd6 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 19 Jan 2024 14:04:48 -0500 Subject: [PATCH 5/6] Test reflects new status of usermeta --- tests/test-one-time-login.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-one-time-login.php b/tests/test-one-time-login.php index fb80983..9c3c353 100644 --- a/tests/test-one-time-login.php +++ b/tests/test-one-time-login.php @@ -112,9 +112,9 @@ public function test_one_time_login_handle_token_invalid_user() { $this->expectExceptionMessage( 'Invalid one-time login token. Try signing in instead?' ); one_time_login_generate_tokens( self::$users['administrator'], 1, false ); $tokens = get_user_meta( self::$users['administrator']->ID, 'one_time_login_token', true ); - $token = array_shift( $tokens ); + $token = reset( $tokens ); $GLOBALS['pagenow'] = 'wp-login.php'; - $_GET['one_time_login_token'] = $token; + $_GET['one_time_login_token'] = $token['token']; $_GET['user_id'] = 999999; one_time_login_handle_token(); } From 3d1230d8c211be06e893d7ad3d0aa7fb0ec4a596 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 19 Jan 2024 14:26:03 -0500 Subject: [PATCH 6/6] Update fix for test --- tests/test-one-time-login.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test-one-time-login.php b/tests/test-one-time-login.php index 9c3c353..1443e91 100644 --- a/tests/test-one-time-login.php +++ b/tests/test-one-time-login.php @@ -112,9 +112,9 @@ public function test_one_time_login_handle_token_invalid_user() { $this->expectExceptionMessage( 'Invalid one-time login token. Try signing in instead?' ); one_time_login_generate_tokens( self::$users['administrator'], 1, false ); $tokens = get_user_meta( self::$users['administrator']->ID, 'one_time_login_token', true ); - $token = reset( $tokens ); + $token = array_shift( $tokens ); $GLOBALS['pagenow'] = 'wp-login.php'; - $_GET['one_time_login_token'] = $token['token']; + $_GET['one_time_login_token'] = $token; $_GET['user_id'] = 999999; one_time_login_handle_token(); } @@ -138,9 +138,9 @@ public function test_one_time_login_handle_token_invalid_token() { public function test_one_time_login_handle_token_success() { one_time_login_generate_tokens( self::$users['administrator'], 1, false ); $tokens = get_user_meta( self::$users['administrator']->ID, 'one_time_login_token', true ); - $token = array_shift( $tokens ); + $token = reset( $tokens ); $GLOBALS['pagenow'] = 'wp-login.php'; - $_GET['one_time_login_token'] = $token; + $_GET['one_time_login_token'] = $token['token']; $_GET['user_id'] = self::$users['administrator']->ID; one_time_login_handle_token(); $this->assertRedirect( admin_url(), 302 );