201 lines
4.6 KiB
PHP
201 lines
4.6 KiB
PHP
<?php
|
|
|
|
namespace Gravity_Forms\Gravity_Forms_RECAPTCHA;
|
|
|
|
use GFCache, GFAddOn;
|
|
|
|
/**
|
|
* Refresh token lock.
|
|
*
|
|
* Hosts the logic required to prevent repeated refresh token requests.
|
|
*
|
|
* @since 1.8.0
|
|
*/
|
|
class Refresh_Lock_Handler {
|
|
|
|
/**
|
|
* How many seconds until the rate limiting cache lock is cleared.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @var integer
|
|
*/
|
|
const RATE_LIMIT_CACHE_EXPIRATION_SECONDS = MINUTE_IN_SECONDS;
|
|
|
|
/**
|
|
* How many failed refresh requests until the refresh is locked becayse of rate limiting.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @var integer
|
|
*/
|
|
const RATE_LIMIT_FAILED_REQUEST_THRESHOLD = 3;
|
|
|
|
/**
|
|
* How many seconds until the refresh in progress cache lock is cleared.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @var integer
|
|
*/
|
|
const REFRESH_IN_PROGRESS_EXPIRATION_SECONDS = MINUTE_IN_SECONDS;
|
|
|
|
/**
|
|
* Failed request count option key.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $failed_requests_count_key = '';
|
|
|
|
/**
|
|
* The refresh in progress cache key.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $refresh_in_progress_lock_key = '';
|
|
|
|
/**
|
|
* The rate limiting cache key.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $rate_limit_lock_key = '';
|
|
|
|
/**
|
|
* The reason why the refreshing could be locked.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @var string
|
|
*/
|
|
public $refresh_lock_reason = '';
|
|
|
|
/**
|
|
* An instance of the add-on.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @var GFAddOn
|
|
*/
|
|
protected $addon;
|
|
|
|
/**
|
|
* Handler constructor.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @param GFAddOn $addon The add-on the handler is being initialized for.
|
|
*/
|
|
public function __construct( $addon ) {
|
|
$this->addon = $addon;
|
|
$slug = $addon->get_slug();
|
|
$this->failed_requests_count_key = $slug . '_failed_refresh_token_requests_count';
|
|
$this->refresh_in_progress_lock_key = $slug . '_refresh_lock';
|
|
$this->rate_limit_lock_key = $slug . '_rate_limit';
|
|
}
|
|
|
|
/**
|
|
* Checks if the rate limit cache key is set, and sets the lock reason if locked.
|
|
*
|
|
* After consecutive failed requests, a cache key is set to prevent more failed requests.
|
|
*
|
|
* @sicne 1.8.0
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function is_rate_limited() {
|
|
$rate_limited = GFCache::get( $this->rate_limit_lock_key, $found );
|
|
if ( $found && $rate_limited ) {
|
|
$this->refresh_lock_reason = 'Refresh token request rate limit reached.';
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if the threshold for failed refresh requests has been reached, sets the cache key if so.
|
|
*
|
|
* @since 1.8.0
|
|
*/
|
|
public function increment_rate_limit() {
|
|
$failed_requests_count = intval( get_option( $this->failed_requests_count_key ) );
|
|
if ( $failed_requests_count >= self::RATE_LIMIT_FAILED_REQUEST_THRESHOLD ) {
|
|
$this->addon->log_debug( __METHOD__ . '(): Rate limit threshold reached, setting rate limit lock.' );
|
|
GFCache::set( $this->rate_limit_lock_key, true, true, self::RATE_LIMIT_CACHE_EXPIRATION_SECONDS );
|
|
update_option( $this->failed_requests_count_key, 0 );
|
|
} else {
|
|
$this->addon->log_debug( __METHOD__ . '(): Increasing failed requests count, current count: ' . $failed_requests_count );
|
|
update_option( $this->failed_requests_count_key, $failed_requests_count + 1 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if there is a request already being made for refreshing the token.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_locked() {
|
|
$locked = GFCache::get( $this->refresh_in_progress_lock_key, $found );
|
|
if ( $found && $locked ) {
|
|
$this->refresh_lock_reason = 'Token Refresh is already in progress.';
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Sets the refresh in progress cache lock.
|
|
*
|
|
* @since 1.8.0
|
|
*/
|
|
public function lock() {
|
|
GFCache::set( $this->refresh_in_progress_lock_key, true, true, self::REFRESH_IN_PROGRESS_EXPIRATION_SECONDS );
|
|
$this->addon->log_debug( __METHOD__ . '(): Refresh in progress lock has been set.' );
|
|
}
|
|
|
|
/**
|
|
* Clears the rate limit lock.
|
|
*
|
|
* @since 1.8.0
|
|
*/
|
|
public function reset_rate_limit() {
|
|
GFCache::delete( $this->rate_limit_lock_key );
|
|
update_option( $this->failed_requests_count_key, 0 );
|
|
$this->addon->log_debug( __METHOD__ . '(): Rate limit lock cleared.' );
|
|
}
|
|
|
|
/**
|
|
* Clears the refresh in progress lock.
|
|
*
|
|
* @since 1.8.0
|
|
*/
|
|
public function release_lock() {
|
|
GFCache::delete( $this->refresh_in_progress_lock_key );
|
|
$this->addon->log_debug( __METHOD__ . '(): Refresh in progress lock cleared.' );
|
|
}
|
|
|
|
/**
|
|
* Checks the token can be refreshed after making sure no locks are in place.
|
|
*
|
|
* @since 1.8.0
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function can_refresh_token() {
|
|
return $this->is_rate_limited() === false && $this->is_locked() === false;
|
|
}
|
|
|
|
}
|