* @since 5.9.15 */ class IpRateLimitIdentity extends BaseObject implements RateLimitInterface { /** * @var int Maximum number of requests allowed within the window * * If not set, the config setting will be used. */ public int $limit; /** * @var int Time window in seconds * * If not set, the config setting will be used. */ public int $window; /** * @var string Cache key prefix for storing allowance data */ public string $keyPrefix; /** * @var string The IP address to rate limit * * If not set, the request’s user IP will be used. */ public string $ip; /** * Returns a new identity configured with the default rate limit settings, or `null` if * is disabled. * * @param string $keyPrefix The cache key prefix for storing allowance data * @return self|null * @since 5.12.0 */ public static function createDefault(string $keyPrefix): ?self { if (!Craft::$app->getConfig()->getGeneral()->defaultRateLimit) { return null; } return new self(['keyPrefix' => $keyPrefix]); } /** * @inheritdoc */ public function init(): void { parent::init(); if (!isset($this->limit) || !isset($this->window)) { $generalConfig = Craft::$app->getConfig()->getGeneral(); $this->limit ??= (int)$generalConfig->defaultRateLimit; $this->window ??= $generalConfig->defaultRateLimitDuration; } $this->ip ??= Craft::$app->getRequest()->getUserIP() ?? 'unknown'; } /** * @inheritdoc */ public function getRateLimit($request, $action): array { return [$this->limit, $this->window]; } /** * @inheritdoc */ public function loadAllowance($request, $action): array { $key = $this->getCacheKey($action); $data = Craft::$app->getCache()->get($key); return $data !== false ? $data : [$this->limit, time()]; } /** * @inheritdoc */ public function saveAllowance($request, $action, $allowance, $timestamp): void { $key = $this->getCacheKey($action); Craft::$app->getCache()->set($key, [$allowance, $timestamp], $this->window); } /** * Generates the cache key for storing rate limit allowance. */ private function getCacheKey(Action $action): string { return sprintf('%s:%s:%s', $this->keyPrefix, $action->getUniqueId(), $this->ip); } }