Skip to content
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

Restrict API requests if the account is not connected #10304

Closed
wants to merge 6 commits into from

Conversation

dpaun1985
Copy link
Contributor

@dpaun1985 dpaun1985 commented Feb 5, 2025

Fixes #9883

Changes proposed in this Pull Request

This PR reduces redundant server requests by restricting unnecessary client-side calls, that can, in some cases, spam the server ( ex. p1730711562014049-slack-C03V0SP07SL ).
To achieve this, requests to all endpoints—except for fetching account details and onboarding—will be restricted when:

  • No account is configured
  • The account is either unlinked or rejected

Testing instructions

Pre-requirements:

  1. Go to Woocommerce -> Settings -> Advanced -> REST API and create api credentials
  2. configure postman to use the credentials
Screenshot 2025-02-05 at 11 46 01

Testing instructions

  1. Without an account connected
  • perform an API request to GET http://localhost:8082/wp-json/wc/v3/payments/transactions/summary?deposit_id=&_locale=user .
  • check the woopayments logs in Woocommerce -> Status -> Logs . The error should be logged.
Screenshot 2025-02-05 at 12 10 21
  • go in the admin panel and perform different actions, like saving settings and check the client/server logs for any unusual behavior
  • the same applies for unlinked or rejected accounts ( accounts with status unlinked or rejected.... )
  1. With an account connected
  • perform an API request to GET http://localhost:8082/wp-json/wc/v3/payments/transactions/summary?deposit_id=&_locale=user .
  • check the woopayments logs in Woocommerce -> Status -> Logs . NO new errors should be logged.
  • go in the admin panel and perform different actions, like saving settings and check the client/server logs for any unusual behavior . check the logs. no new errors related to API request should be logged

Also some regression testing would be good, to be sure that we don't block anything without intent.


  • Run npm run changelog to add a changelog file, choose patch to leave it empty if the change is not significant. You can add multiple changelog files in one PR by running this command a few times.
  • Covered with tests (or have a good reason not to test in description ☝️)
  • Tested on mobile (or does not apply)

Post merge

@botwoo
Copy link
Collaborator

botwoo commented Feb 5, 2025

Test the build

Option 1. Jetpack Beta

  • Install and activate Jetpack Beta.
  • Use this build by searching for PR number 10304 or branch name add/9883-filter-api-requests in your-test.site/wp-admin/admin.php?page=jetpack-beta&plugin=woocommerce-payments

Option 2. Jurassic Ninja - available for logged-in A12s

🚀 Launch a JN site with this branch 🚀

ℹ️ Install this Tampermonkey script to get more options.


Build info:

  • Latest commit: 88d4854
  • Build time: 2025-02-11 12:09:06 UTC

Note: the build is updated when a new commit is pushed to this PR.

Copy link
Contributor

github-actions bot commented Feb 5, 2025

Size Change: -71 kB (-5%) ✅

Total Size: 1.29 MB

Filename Size Change
release/woocommerce-payments/assets/css/admin.css 1.38 kB +11 B (+1%)
release/woocommerce-payments/assets/css/admin.rtl.css 1.38 kB +11 B (+1%)
release/woocommerce-payments/dist/blocks-checkout-rtl.css 2.67 kB +3 B (0%)
release/woocommerce-payments/dist/blocks-checkout.css 2.67 kB +3 B (0%)
release/woocommerce-payments/dist/blocks-checkout.js 55.3 kB +146 B (0%)
release/woocommerce-payments/dist/checkout-rtl.css 1.28 kB +7 B (+1%)
release/woocommerce-payments/dist/checkout.css 1.28 kB +7 B (+1%)
release/woocommerce-payments/dist/checkout.js 34.6 kB +11 B (0%)
release/woocommerce-payments/dist/express-checkout.js 15.7 kB +16 B (0%)
release/woocommerce-payments/dist/index-rtl.css 35.5 kB -4.31 kB (-11%) 👏
release/woocommerce-payments/dist/index.css 35.5 kB -4.22 kB (-11%) 👏
release/woocommerce-payments/dist/index.js 233 kB -66.7 kB (-22%) 🎉
release/woocommerce-payments/dist/multi-currency-switcher-block.js 61.1 kB +21 B (0%)
release/woocommerce-payments/dist/multi-currency.js 59.1 kB +1.29 kB (+2%)
release/woocommerce-payments/dist/order.js 42.5 kB +24 B (0%)
release/woocommerce-payments/dist/payment-gateways.js 40.1 kB +1.26 kB (+3%)
release/woocommerce-payments/dist/settings.js 224 kB +1.38 kB (+1%)
release/woocommerce-payments/dist/tokenized-express-checkout.js 16.6 kB +49 B (0%)
ℹ️ View Unchanged
Filename Size
release/woocommerce-payments/assets/css/success.css 189 B
release/woocommerce-payments/assets/css/success.rtl.css 190 B
release/woocommerce-payments/dist/cart-block.js 17.2 kB
release/woocommerce-payments/dist/cart.js 5.73 kB
release/woocommerce-payments/dist/express-checkout-rtl.css 236 B
release/woocommerce-payments/dist/express-checkout.css 236 B
release/woocommerce-payments/dist/frontend-tracks.js 854 B
release/woocommerce-payments/dist/multi-currency-analytics.js 1.08 kB
release/woocommerce-payments/dist/multi-currency-rtl.css 4.29 kB
release/woocommerce-payments/dist/multi-currency.css 4.29 kB
release/woocommerce-payments/dist/order-rtl.css 740 B
release/woocommerce-payments/dist/order.css 740 B
release/woocommerce-payments/dist/payment-gateways-rtl.css 1.34 kB
release/woocommerce-payments/dist/payment-gateways.css 1.34 kB
release/woocommerce-payments/dist/plugins-page-rtl.css 386 B
release/woocommerce-payments/dist/plugins-page.css 386 B
release/woocommerce-payments/dist/plugins-page.js 20.1 kB
release/woocommerce-payments/dist/product-details-rtl.css 433 B
release/woocommerce-payments/dist/product-details.css 436 B
release/woocommerce-payments/dist/product-details.js 12.5 kB
release/woocommerce-payments/dist/settings-rtl.css 11.5 kB
release/woocommerce-payments/dist/settings.css 11.4 kB
release/woocommerce-payments/dist/subscription-edit-page.js 703 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal-rtl.css 524 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal.css 524 B
release/woocommerce-payments/dist/subscription-product-onboarding-modal.js 20.2 kB
release/woocommerce-payments/dist/subscription-product-onboarding-toast.js 730 B
release/woocommerce-payments/dist/subscriptions-empty-state-rtl.css 120 B
release/woocommerce-payments/dist/subscriptions-empty-state.css 120 B
release/woocommerce-payments/dist/subscriptions-empty-state.js 19.3 kB
release/woocommerce-payments/dist/tokenized-express-checkout-rtl.css 236 B
release/woocommerce-payments/dist/tokenized-express-checkout.css 236 B
release/woocommerce-payments/dist/tos-rtl.css 235 B
release/woocommerce-payments/dist/tos.css 235 B
release/woocommerce-payments/dist/tos.js 21.8 kB
release/woocommerce-payments/dist/woopay-direct-checkout.js 6.13 kB
release/woocommerce-payments/dist/woopay-express-button.js 23.3 kB
release/woocommerce-payments/dist/woopay-rtl.css 4.31 kB
release/woocommerce-payments/dist/woopay.css 4.28 kB
release/woocommerce-payments/dist/woopay.js 71 kB
release/woocommerce-payments/includes/subscriptions/assets/css/plugin-page.css 625 B
release/woocommerce-payments/includes/subscriptions/assets/js/plugin-page.js 814 B
release/woocommerce-payments/vendor/automattic/jetpack-assets/build/i18n-loader.js 2.46 kB
release/woocommerce-payments/vendor/automattic/jetpack-assets/build/jetpack-script-data.js 772 B
release/woocommerce-payments/vendor/automattic/jetpack-assets/src/js/i18n-loader.js 1.02 kB
release/woocommerce-payments/vendor/automattic/jetpack-assets/src/js/script-data.js 69 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/babel.config.js 163 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/identity-crisis.css 2.47 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/identity-crisis.js 14.2 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/identity-crisis.rtl.css 2.47 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-connection.css 10 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-connection.js 28.4 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-connection.rtl.css 10 kB
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-admin-create-user.css 198 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-admin-create-user.js 280 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-admin-create-user.rtl.css 198 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-login.css 625 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-login.js 333 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-login.rtl.css 626 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/jetpack-sso-users.js 424 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/tracks-ajax.js 521 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/dist/tracks-callables.js 585 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-admin-create-user.css 215 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-admin-create-user.js 521 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-login.css 721 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-login.js 412 B
release/woocommerce-payments/vendor/automattic/jetpack-connection/src/sso/jetpack-sso-users.js 632 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/about.css 1.04 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin-empty-state.css 294 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin-order-statuses.css 408 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/admin.css 3.59 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/checkout.css 301 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/modal.css 746 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/view-subscription.css 574 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/css/wcs-upgrade.css 414 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/admin-pointers.js 543 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/admin.js 9.4 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/jstz.js 6.78 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/jstz.min.js 3.84 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/meta-boxes-coupon.js 545 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/meta-boxes-subscription.js 2.52 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/moment.js 22.2 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/moment.min.js 11.7 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/payment-method-restrictions.js 1.29 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/admin/wcs-meta-boxes-order.js 507 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/payment-methods.js 358 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/single-product.js 428 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/view-subscription.js 1.38 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/frontend/wcs-cart.js 782 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/modal.js 1.09 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/assets/js/wcs-upgrade.js 1.26 kB
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/build/index.css 391 B
release/woocommerce-payments/vendor/woocommerce/subscriptions-core/build/index.js 3.04 kB

compressed-size-action

@dpaun1985 dpaun1985 marked this pull request as ready for review February 5, 2025 11:16
@dpaun1985 dpaun1985 requested a review from a team February 5, 2025 11:16
Copy link
Contributor

@oaratovskyi oaratovskyi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The solution LGTM and works well!
I have small doubts whether to log error notices because it's considered normal situation - if account is not connected, we don't make requests to server (except onboarding and account fetch).
I suggested to show it as info level. Also I suggested more clear code comments. Pre-approving, we need only to align on log level and it's good to go from my POV.

@marcinbot could you make quick code-review only since you created this issue? 🙏

changelog/add-9883-filter-api-requests Outdated Show resolved Hide resolved
includes/wc-payment-api/class-wc-payments-api-client.php Outdated Show resolved Hide resolved
includes/wc-payment-api/class-wc-payments-api-client.php Outdated Show resolved Hide resolved
includes/wc-payment-api/class-wc-payments-api-client.php Outdated Show resolved Hide resolved
return true;
}

$account = WC_Payments::get_account_service()->get_cached_account_data();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this method also sends a request so we should test for a scenario where this occurs.

I think something like infinite loop should be prevented with the self::ACCOUNTS_API === $api check above, but we could be potentially sending two requests here.

I think a safer thing to do might be to also check if the $api begins with the ACCOUNTS_API like we do for onboarding. Effectively whitelisting any accounts API calls.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dpaun1985 could you provide testing instructions how to test rejected/unlinked status? I tried updating it in DB, but after clearing account cache it becomes complete again

Copy link
Contributor Author

@dpaun1985 dpaun1985 Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pushed the changes in 88d4854 . thanks @marcinbot & @oaratovskyi

@oaratovskyi from what I could find, for unlinked status, there was a check introduced some months ago, and no account will be returned. For rejected status, I picked an account and linked it to my blog from Stripe dashboard.

Copy link
Contributor

@oaratovskyi oaratovskyi Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @dpaun1985, changes LGTM!

For rejected status, I picked an account and linked it to my blog from Stripe dashboard.

you used console command or MC tool? I will try to reproduce locally

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you used console command or MC tool? I will try to reproduce locally

I used the link-account console command on platform side.

Copy link
Contributor

@vladolaru vladolaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this, @dpaun1985! There is still some work left - see the comments.

Plus, we should have a (couple) of tests (in tests/unit/wc-payment-api/test-class-wc-payments-api-client.php) for the scenarios when requests are allowed and when they are not. I would just go through the various account states and call all public wrapper methods of the WC_Payments_API_Client, asserting which should be let through and which should be blocked. This way we can be confident that our changes don't inadvertently break something.


$account = WC_Payments::get_account_service()->get_cached_account_data();
// If the account is not set, we should not allow any API requests.
if ( ! is_array( $account ) || empty( $account ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about being a little more paranoid here and just check ! is_array( $account ) || empty( $account['status'] ) since the status entry should always be present?

If you agree, the isset( $account['status'] ) below can be removed.

->getMock();
$mock_account_service
->method( 'get_cached_account_data' )
->willReturn( [ 'id' => 1 ] );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably include a status entry also.

->getMock();
$mock_account_service
->method( 'get_cached_account_data' )
->willReturn( [ 'id' => 1 ] );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably include a status entry also.

->getMock();
$mock_account_service
->method( 'get_cached_account_data' )
->willReturn( [ 'id' => 1 ] );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably include a status entry also.

@@ -2119,6 +2119,11 @@ public function send_request( Request $request ) {
* @throws API_Exception - If the account ID hasn't been set.
*/
protected function request( $params, $api, $method, $is_site_specific = true, $use_user_token = false, bool $raw_response = false ) {
if ( ! $this->is_api_request_allowed( $api ) ) {
Logger::info( "Platform API request '$api' wasn't executed. API requests are only performed if the account is connected." );
return []; // this will result as 200 OK with empty array response.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not too happy or confident about returning 200 OK. It doesn't seem like the right thing to do.

To my mind, the caller needs to be informed that the request wasn't allowed rather than tricking it that it went through and there were no results.

I vote for throwing an API_Exception with a 403 HTTP response code because the request shouldn't have happened in the first place (someone didn't check the account state before calling).

I see no problem in throwing an exception and responding with a 403. The caller should be able to handle it - all public wrapper methods (e.g. WC_Payments_API_Client::get_payment_method) declare that they throw API_Exception.

WDYT @marcinbot? Is there something I am missing here?

Copy link
Contributor

@marcinbot marcinbot Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not missing anything and tbh it rang the alarm bells for me as well. I'm just worried that there will be requests that don't catch things, which might lead to fatal errors on websites with no friendly messaging, or even bricking them completely.

TBH I'm not sure now if #9883 was a good idea to begin with: the API client shouldn't be responsible for checking the account status (the circular dependency on the account class is an indicator of this). It also takes control away from the callers. The API client should just be responsible for sending out authenticated HTTP requests.

And also there's too much risk in either returning a 200 or throwing.

The account check should happen in the caller, if necessary. The issue came on the back of one such problems (fetching the disputes on every page load #9716) and was addressed in that way there.

@dpaun1985 @oaratovskyi - would you be ok with closing this PR and #9883 without shipping it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the API client shouldn't be responsible for checking the account status (the circular dependency on the account class is an indicator of this). It also takes control away from the callers. The API client should just be responsible for sending out authenticated HTTP requests.

totally agree with you

would you be ok with closing this PR and #9883 without shipping it?

in this case yes, we can close this issue

thanks for the feedback @marcinbot

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! And I appreciate all the work that went into this! Let's close then.

@dpaun1985
Copy link
Contributor Author

After the discussions above, we've decided to close this issue as it will not be shipped.

@dpaun1985 dpaun1985 closed this Feb 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optimization: API client should only send requests if an account is connected
5 participants