Cherry picked sec changes
This commit is contained in:
parent
e204731617
commit
8d57884a70
|
@ -1135,8 +1135,18 @@ class WC_Checkout {
|
||||||
public function process_checkout() {
|
public function process_checkout() {
|
||||||
try {
|
try {
|
||||||
$nonce_value = wc_get_var( $_REQUEST['woocommerce-process-checkout-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // phpcs:ignore
|
$nonce_value = wc_get_var( $_REQUEST['woocommerce-process-checkout-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // phpcs:ignore
|
||||||
|
$expiry_message = sprintf(
|
||||||
|
/* translators: %s: shop cart url */
|
||||||
|
__( 'Sorry, your session has expired. <a href="%s" class="wc-backward">Return to shop</a>', 'woocommerce' ),
|
||||||
|
esc_url( wc_get_page_permalink( 'shop' ) )
|
||||||
|
);
|
||||||
|
|
||||||
if ( empty( $nonce_value ) || ! wp_verify_nonce( $nonce_value, 'woocommerce-process_checkout' ) ) {
|
if ( empty( $nonce_value ) || ! wp_verify_nonce( $nonce_value, 'woocommerce-process_checkout' ) ) {
|
||||||
|
// If the cart is empty, the nonce check failed because of session expiry.
|
||||||
|
if ( WC()->cart->is_empty() ) {
|
||||||
|
throw new Exception( $expiry_message );
|
||||||
|
}
|
||||||
|
|
||||||
WC()->session->set( 'refresh_totals', true );
|
WC()->session->set( 'refresh_totals', true );
|
||||||
throw new Exception( __( 'We were unable to process your order, please try again.', 'woocommerce' ) );
|
throw new Exception( __( 'We were unable to process your order, please try again.', 'woocommerce' ) );
|
||||||
}
|
}
|
||||||
|
@ -1147,8 +1157,7 @@ class WC_Checkout {
|
||||||
do_action( 'woocommerce_before_checkout_process' );
|
do_action( 'woocommerce_before_checkout_process' );
|
||||||
|
|
||||||
if ( WC()->cart->is_empty() ) {
|
if ( WC()->cart->is_empty() ) {
|
||||||
/* translators: %s: shop cart url */
|
throw new Exception( $expiry_message );
|
||||||
throw new Exception( sprintf( __( 'Sorry, your session has expired. <a href="%s" class="wc-backward">Return to shop</a>', 'woocommerce' ), esc_url( wc_get_page_permalink( 'shop' ) ) ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do_action( 'woocommerce_checkout_process' );
|
do_action( 'woocommerce_checkout_process' );
|
||||||
|
|
|
@ -88,12 +88,18 @@ class WC_Session_Handler extends WC_Session {
|
||||||
$cookie = $this->get_session_cookie();
|
$cookie = $this->get_session_cookie();
|
||||||
|
|
||||||
if ( $cookie ) {
|
if ( $cookie ) {
|
||||||
|
// Customer ID will be an MD5 hash id this is a guest session.
|
||||||
$this->_customer_id = $cookie[0];
|
$this->_customer_id = $cookie[0];
|
||||||
$this->_session_expiration = $cookie[1];
|
$this->_session_expiration = $cookie[1];
|
||||||
$this->_session_expiring = $cookie[2];
|
$this->_session_expiring = $cookie[2];
|
||||||
$this->_has_cookie = true;
|
$this->_has_cookie = true;
|
||||||
$this->_data = $this->get_session_data();
|
$this->_data = $this->get_session_data();
|
||||||
|
|
||||||
|
if ( ! $this->is_session_cookie_valid() ) {
|
||||||
|
$this->destroy_session();
|
||||||
|
$this->set_session_expiration();
|
||||||
|
}
|
||||||
|
|
||||||
// If the user logs in, update session.
|
// If the user logs in, update session.
|
||||||
if ( is_user_logged_in() && strval( get_current_user_id() ) !== $this->_customer_id ) {
|
if ( is_user_logged_in() && strval( get_current_user_id() ) !== $this->_customer_id ) {
|
||||||
$guest_session_id = $this->_customer_id;
|
$guest_session_id = $this->_customer_id;
|
||||||
|
@ -115,6 +121,30 @@ class WC_Session_Handler extends WC_Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if session cookie is expired, or belongs to a logged out user.
|
||||||
|
*
|
||||||
|
* @return bool Whether session cookie is valid.
|
||||||
|
*/
|
||||||
|
private function is_session_cookie_valid() {
|
||||||
|
// If session is expired, session cookie is invalid.
|
||||||
|
if ( time() > $this->_session_expiration ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user has logged out, session cookie is invalid.
|
||||||
|
if ( ! is_user_logged_in() && ! $this->is_customer_guest( $this->_customer_id ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session from a different user is not valid. (Although from a guest user will be valid)
|
||||||
|
if ( is_user_logged_in() && ! $this->is_customer_guest( $this->_customer_id ) && strval( get_current_user_id() ) !== $this->_customer_id ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the session cookie on-demand (usually after adding an item to the cart).
|
* Sets the session cookie on-demand (usually after adding an item to the cart).
|
||||||
*
|
*
|
||||||
|
@ -181,12 +211,54 @@ class WC_Session_Handler extends WC_Session {
|
||||||
if ( empty( $customer_id ) ) {
|
if ( empty( $customer_id ) ) {
|
||||||
require_once ABSPATH . 'wp-includes/class-phpass.php';
|
require_once ABSPATH . 'wp-includes/class-phpass.php';
|
||||||
$hasher = new PasswordHash( 8, false );
|
$hasher = new PasswordHash( 8, false );
|
||||||
$customer_id = md5( $hasher->get_random_bytes( 32 ) );
|
$customer_id = 't_' . substr( md5( $hasher->get_random_bytes( 32 ) ), 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $customer_id;
|
return $customer_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this is an auto-generated customer ID.
|
||||||
|
*
|
||||||
|
* @param string|int $customer_id Customer ID to check.
|
||||||
|
*
|
||||||
|
* @return bool Whether customer ID is randomly generated.
|
||||||
|
*/
|
||||||
|
private function is_customer_guest( $customer_id ) {
|
||||||
|
$customer_id = strval( $customer_id );
|
||||||
|
|
||||||
|
if ( empty( $customer_id ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 't_' === substr( $customer_id, 0, 2 ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy checks. This is to handle sessions that were created from a previous release.
|
||||||
|
* Maybe we can get rid of them after a few releases.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Almost all random $customer_ids will have some letters in it, while all actual ids will be integers.
|
||||||
|
if ( strval( (int) $customer_id ) !== $customer_id ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performance hack to potentially save a DB query, when same user as $customer_id is logged in.
|
||||||
|
if ( is_user_logged_in() && strval( get_current_user_id() ) === $customer_id ) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$customer = new WC_Customer( $customer_id );
|
||||||
|
|
||||||
|
if ( 0 === $customer->get_id() ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get session unique ID for requests if session is initialized or user ID if logged in.
|
* Get session unique ID for requests if session is initialized or user ID if logged in.
|
||||||
* Introduced to help with unit tests.
|
* Introduced to help with unit tests.
|
||||||
|
|
|
@ -130,12 +130,111 @@ class WC_Tests_Session_Handler extends WC_Unit_Test_Case {
|
||||||
$this->assertEquals( $this->handler->get_customer_unique_id(), $this->handler->maybe_update_nonce_user_logged_out( 1, 'woocommerce-something' ) );
|
$this->assertEquals( $this->handler->get_customer_unique_id(), $this->handler->maybe_update_nonce_user_logged_out( 1, 'woocommerce-something' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @testdox Test that session from cookie is destroyed if expired.
|
||||||
|
*/
|
||||||
|
public function test_destroy_session_cookie_expired() {
|
||||||
|
$customer_id = '1';
|
||||||
|
$session_expiration = time() - 10000;
|
||||||
|
$session_expiring = time() - 1000;
|
||||||
|
$cookie_hash = '';
|
||||||
|
$this->session_key = $customer_id;
|
||||||
|
|
||||||
|
$handler = $this
|
||||||
|
->getMockBuilder( WC_Session_Handler::class )
|
||||||
|
->setMethods( array( 'get_session_cookie' ) )
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$handler
|
||||||
|
->method( 'get_session_cookie' )
|
||||||
|
->willReturn( array( $customer_id, $session_expiration, $session_expiring, $cookie_hash ) );
|
||||||
|
|
||||||
|
add_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
|
||||||
|
|
||||||
|
$handler->init_session_cookie();
|
||||||
|
|
||||||
|
remove_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
|
||||||
|
|
||||||
|
$this->assertFalse( wp_cache_get( $this->cache_prefix . $this->session_key, WC_SESSION_CACHE_GROUP ) );
|
||||||
|
$this->assertNull( $this->get_session_from_db( $this->session_key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @testdox Test that session from cookie is destroyed if user is logged out.
|
||||||
|
*/
|
||||||
|
public function test_destroy_session_user_logged_out() {
|
||||||
|
$customer_id = '1';
|
||||||
|
$session_expiration = time() + 50000;
|
||||||
|
$session_expiring = time() + 5000;
|
||||||
|
$cookie_hash = '';
|
||||||
|
$this->session_key = $customer_id;
|
||||||
|
|
||||||
|
// Simulate a log out.
|
||||||
|
wp_set_current_user( 0 );
|
||||||
|
|
||||||
|
$handler = $this
|
||||||
|
->getMockBuilder( WC_Session_Handler::class )
|
||||||
|
->setMethods( array( 'get_session_cookie' ) )
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
$handler
|
||||||
|
->method( 'get_session_cookie' )
|
||||||
|
->willReturn( array( $customer_id, $session_expiration, $session_expiring, $cookie_hash ) );
|
||||||
|
|
||||||
|
add_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
|
||||||
|
|
||||||
|
$handler->init_session_cookie();
|
||||||
|
|
||||||
|
remove_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
|
||||||
|
|
||||||
|
$this->assertFalse( wp_cache_get( $this->cache_prefix . $this->session_key, WC_SESSION_CACHE_GROUP ) );
|
||||||
|
$this->assertNull( $this->get_session_from_db( $this->session_key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @testdox Test that session from cookie is destroyed if logged in user doesn't match.
|
||||||
|
*/
|
||||||
|
public function test_destroy_session_user_mismatch() {
|
||||||
|
$customer = WC_Helper_Customer::create_customer();
|
||||||
|
$customer_id = (string) $customer->get_id();
|
||||||
|
$session_expiration = time() + 50000;
|
||||||
|
$session_expiring = time() + 5000;
|
||||||
|
$cookie_hash = '';
|
||||||
|
|
||||||
|
$handler = $this
|
||||||
|
->getMockBuilder( WC_Session_Handler::class )
|
||||||
|
->setMethods( array( 'get_session_cookie' ) )
|
||||||
|
->getMock();
|
||||||
|
|
||||||
|
wp_set_current_user( $customer->get_id() );
|
||||||
|
|
||||||
|
$handler->init();
|
||||||
|
$handler->set( 'cart', 'fake cart' );
|
||||||
|
$handler->save_data();
|
||||||
|
|
||||||
|
$handler
|
||||||
|
->method( 'get_session_cookie' )
|
||||||
|
->willReturn( array( $customer_id, $session_expiration, $session_expiring, $cookie_hash ) );
|
||||||
|
|
||||||
|
wp_set_current_user( 1 );
|
||||||
|
|
||||||
|
add_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
|
||||||
|
|
||||||
|
$handler->init_session_cookie();
|
||||||
|
|
||||||
|
remove_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
|
||||||
|
|
||||||
|
$this->assertFalse( wp_cache_get( $this->cache_prefix . $customer_id, WC_SESSION_CACHE_GROUP ) );
|
||||||
|
$this->assertNull( $this->get_session_from_db( $customer_id ) );
|
||||||
|
$this->assertNotNull( $this->get_session_from_db( '1' ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to create a WC session and save it to the DB.
|
* Helper function to create a WC session and save it to the DB.
|
||||||
*/
|
*/
|
||||||
protected function create_session() {
|
protected function create_session() {
|
||||||
$this->handler->init();
|
|
||||||
wp_set_current_user( 1 );
|
wp_set_current_user( 1 );
|
||||||
|
$this->handler->init();
|
||||||
$this->handler->set( 'cart', 'fake cart' );
|
$this->handler->set( 'cart', 'fake cart' );
|
||||||
$this->handler->save_data();
|
$this->handler->save_data();
|
||||||
$this->session_key = $this->handler->get_customer_id();
|
$this->session_key = $this->handler->get_customer_id();
|
||||||
|
|
Loading…
Reference in New Issue