Added rate limiting to Add payment method.
This commit is contained in:
parent
e34601458b
commit
0d3074a554
|
@ -453,6 +453,26 @@ class WC_Form_Handler {
|
|||
if ( isset( $_POST['woocommerce_add_payment_method'], $_POST['payment_method'] ) ) {
|
||||
wc_nocache_headers();
|
||||
|
||||
// Test rate limit.
|
||||
$current_user_id = get_current_user_id();
|
||||
$rate_limit_id = 'add_payment_method_' . $current_user_id;
|
||||
$delay = (int) apply_filters( 'woocommerce_payment_gateway_add_payment_method_delay', 20 );
|
||||
|
||||
if ( WC_Rate_Limiter::retried_too_soon( $rate_limit_id ) ) {
|
||||
wc_add_notice(
|
||||
/* translators: %d number of seconds */
|
||||
_n(
|
||||
'You cannot add a new payment method so soon after the previous one. Please wait for %d second.',
|
||||
'You cannot add a new payment method so soon after the previous one. Please wait for %d seconds.',
|
||||
$delay,
|
||||
'woocommerce' ),
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
WC_Rate_Limiter::set_rate_limit( $rate_limit_id, $delay);
|
||||
|
||||
$nonce_value = wc_get_var( $_REQUEST['woocommerce-add-payment-method-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
|
||||
|
||||
if ( ! wp_verify_nonce( $nonce_value, 'woocommerce-add-payment-method' ) ) {
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/**
|
||||
* Provide basic rate limiting functionality via WP Options API.
|
||||
*
|
||||
* Currently only provides a simple limit by delaying action by X seconds.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* When an action runs, call set_rate_limit, e.g.:
|
||||
*
|
||||
* WC_Rate_Limiter::set_rate_limit( "{$my_action_name}_{$user_id}", $delay );
|
||||
*
|
||||
* This sets a timestamp for future timestamp after which action can run again.
|
||||
*
|
||||
*
|
||||
* Then before running the action again, check if the action is allowed to run, e.g.:
|
||||
*
|
||||
* if ( WC_Rate_Limiter::retried_too_soon( "{$my_action_name}_{$user_id}" ) ) {
|
||||
* add_notice( 'Sorry, too soon!' );
|
||||
* }
|
||||
*
|
||||
* @package WooCommerce/Classes
|
||||
* @version 3.9.0
|
||||
* @since 3.9.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Rate limit class.
|
||||
*/
|
||||
class WC_Rate_Limiter {
|
||||
|
||||
/**
|
||||
* Constructs Option name from action identifier.
|
||||
*
|
||||
* @param $id
|
||||
* @return string
|
||||
*/
|
||||
public static function storage_id( $id ) {
|
||||
return 'woocommerce_rate_limit_' . $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the action is not allowed to be run by the rate limiter yet, false otherwise.
|
||||
*
|
||||
* @param $id Identifier for the action
|
||||
* @return bool
|
||||
*/
|
||||
public static function retried_too_soon( $id ) {
|
||||
$next_try_allowed_at = get_option( self::storage_id( $id ) );
|
||||
|
||||
// No record of action running, so action is allowed to run.
|
||||
if ( false === $next_try_allowed_at ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Before allowed next run, retry not allowed yet.
|
||||
if ( time() <= $next_try_allowed_at ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// After allowed next run, retry allowed.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the rate limit delay in seconds for action with identifier $id.
|
||||
*
|
||||
* @param $id Identifier for the action.
|
||||
* @param $delay Delay in seconds.
|
||||
* @return bool True if the option setting was successful, false otherwise.
|
||||
*/
|
||||
public static function set_rate_limit( $id, $delay ) {
|
||||
$option_name = self::storage_id( $id );
|
||||
$next_try_allowed_at = time() + $delay;
|
||||
return update_option( $option_name, $next_try_allowed_at );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/**
|
||||
* File for the WC_Rate_Limiter class.
|
||||
*
|
||||
* @package WooCommerce\Tests\Util
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test class for WC_Rate_Limiter.
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class WC_Tests_Rate_Limiter extends WC_Unit_Test_Case {
|
||||
|
||||
|
||||
/**
|
||||
* Run setup code for unit tests.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run tear down code for unit tests.
|
||||
*/
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting the limit and running rate limited actions.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public function test_rate_limit_limits() {
|
||||
$action_identifier = 'action_1';
|
||||
$user_1_id = 10;
|
||||
$user_2_id = 15;
|
||||
|
||||
$rate_limit_id_1 = $action_identifier . $user_1_id;
|
||||
$rate_limit_id_2 = $action_identifier . $user_2_id;
|
||||
|
||||
WC_Rate_Limiter::set_rate_limit( $rate_limit_id_1, 1 );
|
||||
|
||||
$this->assertEquals( true, WC_Rate_Limiter::retried_too_soon( $rate_limit_id_1 ), 'retried_too_soon allowed action to run too soon.' );
|
||||
$this->assertEquals( false, WC_Rate_Limiter::retried_too_soon( $rate_limit_id_2 ), 'retried_too_soon did not allow action to run for another user.' );
|
||||
|
||||
sleep(1);
|
||||
$this->assertEquals( true, WC_Rate_Limiter::retried_too_soon( $rate_limit_id_1 ), 'retried_too_soon did not allow action to run after the designated delay.' );
|
||||
$this->assertEquals( true, WC_Rate_Limiter::retried_too_soon( $rate_limit_id_2 ), 'retried_too_soon did not allow action to run for another user.' );
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue