Create authorization system

This commit is contained in:
2026-01-05 23:31:36 +03:00
parent d438f7bf5b
commit 2e14a7f364
39 changed files with 2500 additions and 9 deletions
+36
View File
@@ -0,0 +1,36 @@
from typing import Dict, Optional
from fastapi import HTTPException, Request, status
from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel
from fastapi.security import OAuth2
from fastapi.security.utils import get_authorization_scheme_param
class OAuth2PasswordBearerWithCookie(OAuth2):
def __init__(
self,
tokenUrl: str,
scheme_name: Optional[str] = None,
scopes: Optional[Dict[str, str]] = None,
auto_error: bool = True,
):
if not scopes:
scopes = {}
flows = OAuthFlowsModel(
password={"tokenUrl": tokenUrl, "scopes": scopes})
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
async def __call__(self, request: Request) -> Optional[str]:
authorization: str = request.cookies.get("access_token")
scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer":
if self.auto_error:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
else:
return None
return param
+27
View File
@@ -0,0 +1,27 @@
import json
from functools import wraps
from fastapi import Request
from app.utils.redis import get_redis
def cache(ttl: int = 10):
if ttl <= 0:
raise ValueError("TTL must be greater than zero.")
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
redis = await get_redis()
request: Request = kwargs.get("request")
response_cache = await redis.get(str(request.url))
if response_cache is not None:
return json.loads(response_cache)
response_cache = await func(*args, **kwargs)
await redis.setex(str(request.url), ttl, json.dumps(response_cache))
return response_cache
return wrapper
return decorator
+11
View File
@@ -0,0 +1,11 @@
from celery import Celery
from app.config import settings
celery_app = Celery(
"app.utils.celery_app",
broker=settings.RABBITMQ_URL,
backend="rpc://"
)
celery_app.autodiscover_tasks(["app.tasks"])
+46
View File
@@ -0,0 +1,46 @@
import smtplib
import logging
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from jinja2 import Environment, FileSystemLoader
from app.config import settings
log = logging.getLogger(__name__)
class EmailClient:
env = Environment(
loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), "..", "templates"))
)
@classmethod
def render(cls, template_path, **kwargs):
log.debug(f"Rendering {template_path}", extra={"kwargs": kwargs, "template_path": template_path})
template = cls.env.get_template(template_path)
return template.render(**kwargs)
@classmethod
def send_email(cls, to: str, subject: str, html: str, body: str):
log.info("Sending email", extra={"subject": subject, "to": to})
try:
msg = MIMEMultipart()
msg["Subject"] = subject
msg["From"] = settings.SMTP_EMAIL
msg["To"] = to
msg.attach(MIMEText(html, "html", "utf-8"))
msg.attach(MIMEText(body, "plain", "utf-8"))
with smtplib.SMTP(settings.SMTP_SERVER, settings.SMTP_PORT) as smtp:
if not settings.MODE == "DEV":
smtp.starttls()
smtp.login(settings.SMTP_EMAIL, settings.SMTP_PASS)
smtp.send_message(msg)
log.info("Email sent successfully", extra={"to": to, "subject": subject})
except Exception as e:
log.error(f"Failed to send email: {str(e)}", extra={"to": to, "subject": subject})
raise e
+10
View File
@@ -0,0 +1,10 @@
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password):
return pwd_context.hash(password)
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
+22
View File
@@ -0,0 +1,22 @@
from redis.asyncio import Redis, from_url
from app.config import settings
redis_client: Redis = None
async def init_redis() -> None:
global redis_client
redis_client = await from_url(
settings.REDIS_URL,
encoding="utf-8",
decode_responses=True
)
async def close_redis() -> None:
if redis_client:
await redis_client.close()
async def get_redis() -> Redis:
return redis_client