New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prevent orders being placed when no shipping options are available #46026
Conversation
Hi @senadir, Apart from reviewing the code changes, please make sure to review the testing instructions as well. You can follow this guide to find out what good testing instructions should look like: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is in the right direction, I do think we should be more defensive in validate_order_before_payment
.
For the test, I left some comments but overall I think it's fine. We might need to refactor it in the future to ensure it's as close as possible to real life session manager to avoid false positives.
@@ -168,7 +168,7 @@ public function sync_customer_data_with_order( \WC_Order $order ) { | |||
*/ | |||
public function validate_order_before_payment( \WC_Order $order ) { | |||
$needs_shipping = wc()->cart->needs_shipping(); | |||
$chosen_shipping_methods = wc()->session->get( 'chosen_shipping_methods' ); | |||
$chosen_shipping_methods = wc()->session->get( 'chosen_shipping_methods', [ '' ] ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's best to have this as an empty array and strengthen the checks in validate_selected_shipping_methods
by separating the check for need_shipping out, and doing array validation second.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
public function init_session_cookie( $set = true ) { | ||
$to_hash = $this->_customer_id . '|' . $this->_session_expiration; | ||
$cookie_hash = hash_hmac( 'md5', $to_hash, wp_hash( $to_hash ) ); | ||
$cookie_value = $this->_customer_id . '||' . $this->_session_expiration . '||' . $this->_session_expiring . '||' . $cookie_hash; | ||
$this->_has_cookie = true; | ||
|
||
if ( ! isset( $_COOKIE[ $this->_cookie ] ) || $_COOKIE[ $this->_cookie ] !== $cookie_value ) { | ||
|
||
// Manually set the cookie here because the response is already sent, so wc_setcookie() (as in the parent class) won't work. | ||
$_COOKIE[ $this->_cookie ] = $cookie_value; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a bit risky because it's overreaching, in this case you're replacing init_session_cookie
but are you handling the process of moving from guest to logged in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored now. The functions are the same as the WC_Session_Handler except instead of using wp_cache it uses a mock cache. For some reason caching was preventing the switch from logged out to logged in customer from working.
During this switch, the session data is saved in cache and re-read later when setting the session up for a logged-in user. In tests the cache was not saving it.
// We need to replace the WC_Session with a mock because this test relies on cookies being set which | ||
// is not easy with PHPUnit. This is a simpler approach. | ||
$old_session = WC()->session; | ||
WC()->session = new MockSessionHandler(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the woocommerce_session_handler
filter didn't work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, afraid not the only place to set this up was in plugins/woocommerce/tests/legacy/bootstrap.php
which would affect all tests, not just the ones we need it for. This is a good solution for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Working fine! thank you for doing the changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes!
We might need to look into the future of ways to reduce the size of the session handler.
…46026) * Prevent orders being placed with invalid shipping options * Add changelog * Add shipping_disable_flat_rate fixture function * Test checking out with no valid shipping methods selected * Update tests to add a default shipping method * Update test_checkout_invalid_shipping_method to disable method * If shipping methods is null, return an array with an empty string inside * Replace WC session in tests that rely on setting cookies * Add MockSessionHandler to handle test cases using cookies * Add docblock comment * Expect shipping validation to fail if chosen methods are null * Add shipping method before testing validate_selected_shipping_methods * Update MockSessionHandler to handle caching * Show error when test fails * Default the chosen shipping methods to an empty array if not set * Split checks for needs_shipping and valid shipping apart * Remove unnecessary session set and total calculation * Fix lint errors * Init session in each test * Reimplement required methods (those that are private or use cookies) * Update phpcs ignore comment to be inline * Prevent error when accessing unset variable in mock cache * Fix lint error
* Prevent orders being placed when no shipping options are available (#46026) * Prevent orders being placed with invalid shipping options * Add changelog * Add shipping_disable_flat_rate fixture function * Test checking out with no valid shipping methods selected * Update tests to add a default shipping method * Update test_checkout_invalid_shipping_method to disable method * If shipping methods is null, return an array with an empty string inside * Replace WC session in tests that rely on setting cookies * Add MockSessionHandler to handle test cases using cookies * Add docblock comment * Expect shipping validation to fail if chosen methods are null * Add shipping method before testing validate_selected_shipping_methods * Update MockSessionHandler to handle caching * Show error when test fails * Default the chosen shipping methods to an empty array if not set * Split checks for needs_shipping and valid shipping apart * Remove unnecessary session set and total calculation * Fix lint errors * Init session in each test * Reimplement required methods (those that are private or use cookies) * Update phpcs ignore comment to be inline * Prevent error when accessing unset variable in mock cache * Fix lint error * Prep for cherry pick 46026 --------- Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com> Co-authored-by: WooCommerce Bot <no-reply@woo.com>
…46026) * Prevent orders being placed with invalid shipping options * Add changelog * Add shipping_disable_flat_rate fixture function * Test checking out with no valid shipping methods selected * Update tests to add a default shipping method * Update test_checkout_invalid_shipping_method to disable method * If shipping methods is null, return an array with an empty string inside * Replace WC session in tests that rely on setting cookies * Add MockSessionHandler to handle test cases using cookies * Add docblock comment * Expect shipping validation to fail if chosen methods are null * Add shipping method before testing validate_selected_shipping_methods * Update MockSessionHandler to handle caching * Show error when test fails * Default the chosen shipping methods to an empty array if not set * Split checks for needs_shipping and valid shipping apart * Remove unnecessary session set and total calculation * Fix lint errors * Init session in each test * Reimplement required methods (those that are private or use cookies) * Update phpcs ignore comment to be inline * Prevent error when accessing unset variable in mock cache * Fix lint error
…46026) * Prevent orders being placed with invalid shipping options * Add changelog * Add shipping_disable_flat_rate fixture function * Test checking out with no valid shipping methods selected * Update tests to add a default shipping method * Update test_checkout_invalid_shipping_method to disable method * If shipping methods is null, return an array with an empty string inside * Replace WC session in tests that rely on setting cookies * Add MockSessionHandler to handle test cases using cookies * Add docblock comment * Expect shipping validation to fail if chosen methods are null * Add shipping method before testing validate_selected_shipping_methods * Update MockSessionHandler to handle caching * Show error when test fails * Default the chosen shipping methods to an empty array if not set * Split checks for needs_shipping and valid shipping apart * Remove unnecessary session set and total calculation * Fix lint errors * Init session in each test * Reimplement required methods (those that are private or use cookies) * Update phpcs ignore comment to be inline * Prevent error when accessing unset variable in mock cache * Fix lint error
…46026) * Prevent orders being placed with invalid shipping options * Add changelog * Add shipping_disable_flat_rate fixture function * Test checking out with no valid shipping methods selected * Update tests to add a default shipping method * Update test_checkout_invalid_shipping_method to disable method * If shipping methods is null, return an array with an empty string inside * Replace WC session in tests that rely on setting cookies * Add MockSessionHandler to handle test cases using cookies * Add docblock comment * Expect shipping validation to fail if chosen methods are null * Add shipping method before testing validate_selected_shipping_methods * Update MockSessionHandler to handle caching * Show error when test fails * Default the chosen shipping methods to an empty array if not set * Split checks for needs_shipping and valid shipping apart * Remove unnecessary session set and total calculation * Fix lint errors * Init session in each test * Reimplement required methods (those that are private or use cookies) * Update phpcs ignore comment to be inline * Prevent error when accessing unset variable in mock cache * Fix lint error
…46026) * Prevent orders being placed with invalid shipping options * Add changelog * Add shipping_disable_flat_rate fixture function * Test checking out with no valid shipping methods selected * Update tests to add a default shipping method * Update test_checkout_invalid_shipping_method to disable method * If shipping methods is null, return an array with an empty string inside * Replace WC session in tests that rely on setting cookies * Add MockSessionHandler to handle test cases using cookies * Add docblock comment * Expect shipping validation to fail if chosen methods are null * Add shipping method before testing validate_selected_shipping_methods * Update MockSessionHandler to handle caching * Show error when test fails * Default the chosen shipping methods to an empty array if not set * Split checks for needs_shipping and valid shipping apart * Remove unnecessary session set and total calculation * Fix lint errors * Init session in each test * Reimplement required methods (those that are private or use cookies) * Update phpcs ignore comment to be inline * Prevent error when accessing unset variable in mock cache * Fix lint error
…46026) * Prevent orders being placed with invalid shipping options * Add changelog * Add shipping_disable_flat_rate fixture function * Test checking out with no valid shipping methods selected * Update tests to add a default shipping method * Update test_checkout_invalid_shipping_method to disable method * If shipping methods is null, return an array with an empty string inside * Replace WC session in tests that rely on setting cookies * Add MockSessionHandler to handle test cases using cookies * Add docblock comment * Expect shipping validation to fail if chosen methods are null * Add shipping method before testing validate_selected_shipping_methods * Update MockSessionHandler to handle caching * Show error when test fails * Default the chosen shipping methods to an empty array if not set * Split checks for needs_shipping and valid shipping apart * Remove unnecessary session set and total calculation * Fix lint errors * Init session in each test * Reimplement required methods (those that are private or use cookies) * Update phpcs ignore comment to be inline * Prevent error when accessing unset variable in mock cache * Fix lint error
…46026) * Prevent orders being placed with invalid shipping options * Add changelog * Add shipping_disable_flat_rate fixture function * Test checking out with no valid shipping methods selected * Update tests to add a default shipping method * Update test_checkout_invalid_shipping_method to disable method * If shipping methods is null, return an array with an empty string inside * Replace WC session in tests that rely on setting cookies * Add MockSessionHandler to handle test cases using cookies * Add docblock comment * Expect shipping validation to fail if chosen methods are null * Add shipping method before testing validate_selected_shipping_methods * Update MockSessionHandler to handle caching * Show error when test fails * Default the chosen shipping methods to an empty array if not set * Split checks for needs_shipping and valid shipping apart * Remove unnecessary session set and total calculation * Fix lint errors * Init session in each test * Reimplement required methods (those that are private or use cookies) * Update phpcs ignore comment to be inline * Prevent error when accessing unset variable in mock cache * Fix lint error
Submission Review Guidelines:
Changes proposed in this Pull Request:
Adds a check to the
OrderController
'svalidate_selected_shipping_methods
method to check if the selected shipping method is a valid method.In https://github.com/woocommerce/woocommerce/pull/43869/files#diff-cdc9f1829f52d7b83e3542948622d924ef5cf6a3e0b4580e6ebd1c9643bc49b4R501 we made some changes to shipping method handling to prevent fatal errors, the chosen method was cast to a string in this PR.
This meant our check in
validate_selected_shipping_methods
no longer worked because we are checking for:if ( false === $chosen_shipping_method )
which would no longer be the case.In this PR I am validating a little further by checking the selected method against the available methods.
We set the default value of the
chosen_shipping_method
session variable to[]
when validating orders. This means if it's unset, and shipping is required, sessions with no value will not be allowed to sneak through.This PR brings some changes to PHP unit tests. In the Checkout route unit tests, we now add a Flat Rate shipping method before each test (and remove it afterwards). This is because previously we were using the legacy flat rate which didn't need to be part of a zone to work. This meant that it was not possible to test the fix for this bug as it requires a zone to have a (disabled) shipping method.
We also discovered an issue with the
test_checkout_force_create_account
,test_checkout_do_not_create_account
, andtest_checkout_create_account
tests. They were passing even though no valid shipping method was selected. This PR fixes that by introducing a new class to overrideWC()->session
called.MockSessionHandler
.In
MockSessionHandler
I changed places wherewp_set_cache
was used and implemented a class property to act as the cache, and also overrode theset
function to automatically update the cache when setting data. This was required as the$this->_data
was overwritten with blank data from the empty cache when logging in as a user. The session was never saved to the DB as the PHPUnit tests run in a single context, so by the timeshutdown
runs the test is over.In these tests that use it, we are explicitly overriding WC()->session as there is no good way of setting this up using the
woocommerce_session_handler
filter on a per-test basis.These tests need special handling as they need to be able to set a cookie during guest -> logged in user switching.
Closes #45912.
How to test the changes in this Pull Request:
Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:
Regression testing
Changelog entry
Significance
Type
Message
Comment