mirror of
https://github.com/lorsanstand/Aether.git
synced 2026-06-19 12:05:16 +03:00
Create authorization system
This commit is contained in:
@@ -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
|
||||
Executable
+27
@@ -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
|
||||
@@ -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"])
|
||||
@@ -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
|
||||
@@ -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)
|
||||
Executable
+22
@@ -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
|
||||
Reference in New Issue
Block a user