mirror of
https://github.com/makeplane/plane.git
synced 2026-07-09 20:10:06 +02:00
refactor(api): source API_KEY_RATE_LIMIT from settings, drop service token throttle (#9161)
- Define API_KEY_RATE_LIMIT in plane/settings/common.py and read it via django.conf.settings in ApiKeyRateThrottle instead of os.environ. - Remove ServiceTokenRateThrottle and the service-token branch in BaseAPIView.get_throttles; all API key requests now go through ApiKeyRateThrottle.
This commit is contained in:
committed by
GitHub
parent
f14451a5de
commit
248f5d66e6
@@ -2,8 +2,8 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# See the LICENSE file for details.
|
||||
|
||||
# python imports
|
||||
import os
|
||||
# Django imports
|
||||
from django.conf import settings
|
||||
|
||||
# Third party imports
|
||||
from rest_framework.throttling import SimpleRateThrottle
|
||||
@@ -11,48 +11,7 @@ from rest_framework.throttling import SimpleRateThrottle
|
||||
|
||||
class ApiKeyRateThrottle(SimpleRateThrottle):
|
||||
scope = "api_key"
|
||||
rate = os.environ.get("API_KEY_RATE_LIMIT", "60/minute")
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
# Retrieve the API key from the request header
|
||||
api_key = request.headers.get("X-Api-Key")
|
||||
if not api_key:
|
||||
return None # Allow the request if there's no API key
|
||||
|
||||
# Use the API key as part of the cache key
|
||||
return f"{self.scope}:{api_key}"
|
||||
|
||||
def allow_request(self, request, view):
|
||||
allowed = super().allow_request(request, view)
|
||||
|
||||
if allowed:
|
||||
now = self.timer()
|
||||
# Calculate the remaining limit and reset time
|
||||
history = self.cache.get(self.key, [])
|
||||
|
||||
# Remove old histories
|
||||
while history and history[-1] <= now - self.duration:
|
||||
history.pop()
|
||||
|
||||
# Calculate the requests
|
||||
num_requests = len(history)
|
||||
|
||||
# Check available requests
|
||||
available = self.num_requests - num_requests
|
||||
|
||||
# Unix timestamp for when the rate limit will reset
|
||||
reset_time = int(now + self.duration)
|
||||
|
||||
# Add headers
|
||||
request.META["X-RateLimit-Remaining"] = max(0, available)
|
||||
request.META["X-RateLimit-Reset"] = reset_time
|
||||
|
||||
return allowed
|
||||
|
||||
|
||||
class ServiceTokenRateThrottle(SimpleRateThrottle):
|
||||
scope = "service_token"
|
||||
rate = "300/minute"
|
||||
rate = settings.API_KEY_RATE_LIMIT
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
# Retrieve the API key from the request header
|
||||
|
||||
@@ -22,9 +22,8 @@ from rest_framework.exceptions import APIException
|
||||
from rest_framework.generics import GenericAPIView
|
||||
|
||||
# Module imports
|
||||
from plane.db.models.api import APIToken
|
||||
from plane.api.middleware.api_authentication import APIKeyAuthentication
|
||||
from plane.api.rate_limit import ApiKeyRateThrottle, ServiceTokenRateThrottle
|
||||
from plane.api.rate_limit import ApiKeyRateThrottle
|
||||
from plane.utils.exception_logger import log_exception
|
||||
from plane.utils.paginator import BasePaginator
|
||||
from plane.utils.core.mixins import ReadReplicaControlMixin
|
||||
@@ -60,19 +59,7 @@ class BaseAPIView(TimezoneMixin, GenericAPIView, ReadReplicaControlMixin, BasePa
|
||||
return queryset
|
||||
|
||||
def get_throttles(self):
|
||||
throttle_classes = []
|
||||
api_key = self.request.headers.get("X-Api-Key")
|
||||
|
||||
if api_key:
|
||||
service_token = APIToken.objects.filter(token=api_key, is_service=True).first()
|
||||
|
||||
if service_token:
|
||||
throttle_classes.append(ServiceTokenRateThrottle())
|
||||
return throttle_classes
|
||||
|
||||
throttle_classes.append(ApiKeyRateThrottle())
|
||||
|
||||
return throttle_classes
|
||||
return [ApiKeyRateThrottle()]
|
||||
|
||||
def handle_exception(self, exc):
|
||||
"""
|
||||
|
||||
@@ -132,6 +132,9 @@ REST_FRAMEWORK = {
|
||||
"SCHEMA_COERCE_PATH_PK": False,
|
||||
}
|
||||
|
||||
# API key throttle rate (DRF SimpleRateThrottle format, e.g. "60/minute")
|
||||
API_KEY_RATE_LIMIT = os.environ.get("API_KEY_RATE_LIMIT", "60/minute")
|
||||
|
||||
# Django Auth Backend
|
||||
AUTHENTICATION_BACKENDS = ("django.contrib.auth.backends.ModelBackend",) # default
|
||||
|
||||
|
||||
Reference in New Issue
Block a user