Update: Add rate limiter storage uri and admin adjustment

oh_security
TAMDeveloper13 4 months ago
parent 89662b535b
commit f74f6b6b4c

42
poetry.lock generated

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand.
[[package]]
name = "absl-py"
@ -200,6 +200,19 @@ doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)",
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21.0b1) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\""]
trio = ["trio (>=0.26.1)"]
[[package]]
name = "async-timeout"
version = "5.0.1"
description = "Timeout context manager for asyncio programs"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version == \"3.11\" and python_full_version < \"3.11.3\""
files = [
{file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
{file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
]
[[package]]
name = "asyncpg"
version = "0.30.0"
@ -725,7 +738,7 @@ requests = ">=2.18.0,<3.0.0"
[package.extras]
async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"]
grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev) ; python_version >= \"3.11\"", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0) ; python_version >= \"3.11\""]
grpc = ["grpcio (>=1.33.2,<2.0.dev0)", "grpcio (>=1.49.1,<2.0.dev0) ; python_version >= \"3.11\"", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0) ; python_version >= \"3.11\""]
grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
@ -2205,6 +2218,29 @@ files = [
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
name = "redis"
version = "7.3.0"
description = "Python client for Redis database and key-value store"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "redis-7.3.0-py3-none-any.whl", hash = "sha256:9d4fcb002a12a5e3c3fbe005d59c48a2cc231f87fbb2f6b70c2d89bb64fec364"},
{file = "redis-7.3.0.tar.gz", hash = "sha256:4d1b768aafcf41b01022410b3cc4f15a07d9b3d6fe0c66fc967da2c88e551034"},
]
[package.dependencies]
async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""}
[package.extras]
circuit-breaker = ["pybreaker (>=1.4.0)"]
hiredis = ["hiredis (>=3.2.0)"]
jwt = ["pyjwt (>=2.9.0)"]
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (>=20.0.1)", "requests (>=2.31.0)"]
otel = ["opentelemetry-api (>=1.39.1)", "opentelemetry-exporter-otlp-proto-http (>=1.39.1)", "opentelemetry-sdk (>=1.39.1)"]
xxhash = ["xxhash (>=3.6.0,<3.7.0)"]
[[package]]
name = "requests"
version = "2.32.3"
@ -3046,4 +3082,4 @@ propcache = ">=0.2.1"
[metadata]
lock-version = "2.1"
python-versions = "^3.11"
content-hash = "256c8104c6eeb5b288dd0cdf02fe7cbad4f75aa93fc71f8d44da8b605d72f886"
content-hash = "99817513416e8b654de085dac8ffa606f822d7f03cd12d553c0511298e823986"

@ -33,6 +33,7 @@ google-auth-httplib2 = "^0.2.0"
google-auth-oauthlib = "^1.2.2"
aiohttp = "^3.12.14"
ortools = "^9.14.6206"
redis = "^7.3.0"
[build-system]

@ -61,8 +61,9 @@ class JWTBearer(HTTPBearer):
def verify_jwt(self, jwtoken: str, method: str, endpoint: str):
try:
url_to_verify = f"{config.AUTH_SERVICE_API}/verify-token"
response = requests.get(
f"{config.AUTH_SERVICE_API}/verify-token",
url_to_verify,
headers={"Authorization": f"Bearer {jwtoken}"},
)
@ -127,8 +128,9 @@ async def internal_key(request: Request):
raise Exception(str(e))
else:
try:
verify_url = f"{config.AUTH_SERVICE_API}/verify-token"
response = requests.get(
f"{config.AUTH_SERVICE_API}/verify-token",
verify_url,
headers={"Authorization": f"{token}"},
)
@ -147,7 +149,7 @@ async def internal_key(request: Request):
import httpx
import asyncio
import logging
from typing import Dict, Any
import src.config as config
@ -162,6 +164,7 @@ AUTH_NOTIFY_ENDPOINT = f"{config.AUTH_SERVICE_API}/admin/notify-limit"
async def notify_admin_on_rate_limit(
endpoint_name: str,
ip_address: str,
request: Request,
method: str = "POST",
cooldown: int = 900,
timeout: int = 5
@ -172,21 +175,23 @@ async def notify_admin_on_rate_limit(
Async version - gunakan di async context.
"""
payload = {
"endpoint_name": endpoint_name,
"endpoint_name": f"oh/{endpoint_name}".replace("//", ""),
"ip_address": ip_address,
"method": method,
"cooldown": cooldown,
}
token = request.headers.get("Authorization")
headers = {"Authorization": token} if token else {}
try:
async with httpx.AsyncClient(timeout=timeout) as client:
response = await client.post(AUTH_NOTIFY_ENDPOINT, json=payload)
response.raise_for_status()
result = response.json()
log.info(f"Notifikasi admin sent | Endpoint: {endpoint_name}")
return result
response = await asyncio.to_thread(
requests.post, AUTH_NOTIFY_ENDPOINT,
json=payload, headers=headers, timeout=timeout
)
response.raise_for_status()
result = response.json()
log.info(f"Notifikasi admin sent | Endpoint: {endpoint_name}")
return result
except Exception as e:
log.error(f"Error notifying admin: {str(e)}")
@ -198,6 +203,7 @@ async def notify_admin_on_rate_limit(
def notify_admin_on_rate_limit_sync(
endpoint_name: str,
ip_address: str,
request: Request,
method: str = "POST",
cooldown: int = 900,
timeout: int = 5
@ -209,21 +215,21 @@ def notify_admin_on_rate_limit_sync(
RECOMMENDED untuk use case ini.
"""
payload = {
"endpoint_name": endpoint_name,
"endpoint_name": f"oh/{endpoint_name}".replace("//", "/"),
"ip_address": ip_address,
"method": method,
"cooldown": cooldown,
}
token = request.headers.get("Authorization")
headers = {"Authorization": token} if token else {}
try:
response = httpx.post(AUTH_NOTIFY_ENDPOINT, json=payload, timeout=timeout)
response = requests.post(AUTH_NOTIFY_ENDPOINT, json=payload, headers=headers, timeout=timeout)
response.raise_for_status()
result = response.json()
log.info(f"Notifikasi admin sent | Endpoint: {endpoint_name}")
return result
except Exception as e:
log.error(f"Error notifying admin: {str(e)}")
return {"status": False, "message": str(e), "data": payload}

@ -92,4 +92,5 @@ API_KEY = config("API_KEY")
TR_RBD_ID = config("TR_RBD_ID")
TC_RBD_ID = config("TC_RBD_ID")
DEFAULT_TC_ID = config("DEFAULT_TC_ID")
DEFAULT_TC_ID = config("DEFAULT_TC_ID")
RATELIMIT_STORAGE_URI = f"redis://{config('REDIS_HOST')}:{config('REDIS_PORT')}"

@ -1,4 +1,9 @@
from enum import StrEnum
import sys
if sys.version_info >= (3, 11):
from enum import StrEnum
else:
from strenum import StrEnum
class OptimumOHEnum(StrEnum):

@ -119,6 +119,7 @@ def handle_exception(request: Request, exc: Exception):
endpoint_name=request_info["endpoint"],
ip_address=request_info["remote_addr"],
method=request_info["method"],
request=request,
)
log.warning(

@ -1,8 +1,10 @@
from slowapi import Limiter
from slowapi.util import get_remote_address
from src.config import RATELIMIT_STORAGE_URI
limiter = Limiter(
key_func=get_remote_address,
default_limits=["120 per hour", "2880 per day"],
default_limits=["1 per hour", "2880 per day"],
strategy="fixed-window",
)
storage_uri=RATELIMIT_STORAGE_URI,
)
Loading…
Cancel
Save