diff --git a/fill_db.py b/fill_db.py index 8067791..d794e88 100644 --- a/fill_db.py +++ b/fill_db.py @@ -5,6 +5,7 @@ from main import AUTH_DB_ENGINE, TgAgent, Ref, Sale, Transaction, Account from sqlalchemy import text from datetime import datetime, timedelta from hashlib import sha256 +from passlib.context import CryptContext # Константа: список user_ids @@ -62,6 +63,11 @@ LOGINS = [ ALL_DESCRIPTIONS = DESCRIPTIONS # --- +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +def get_password_hash(password): + return pwd_context.hash(password) + def get_date_list(days=7): today = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0) return [today - timedelta(days=i) for i in range(days, -1, -1)] @@ -80,7 +86,7 @@ def fill_db(): for i in range(4): acc = Account( login=f"user{i+1}", - password="password123", # В реальном проекте пароли должны быть захешированы! + password_hash=get_password_hash("password123"), # теперь храним хеш name=NAMES[i % len(NAMES)], email=f"user{i+1}@example.com", balance=round(random.uniform(1000, 10000), 2) diff --git a/main.py b/main.py index 9205238..da20bad 100644 --- a/main.py +++ b/main.py @@ -9,6 +9,7 @@ from uuid import uuid4 from fastapi.responses import JSONResponse from sqlalchemy import func from hashlib import sha256 +import jwt # Конфигурация AUTH_DATABASE_ADDRESS = "sqlite:///partner.db" @@ -54,7 +55,7 @@ class Transaction(SQLModel, table=True): class Account(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) login: str = Field(index=True, unique=True) - password: str + password_hash: str # теперь хранится hash пароля name: Optional[str] = None email: Optional[str] = None balance: float = 0.0 @@ -127,25 +128,41 @@ def authenticate_tg_agent(engine, tg_id: int): return None return tg_agent -# Защищённый эндпоинт -@app.get("/protected", tags=["partner-tg"]) -def protected_route(current_tg_agent: TgAgent = Depends(get_current_tg_agent)): - return {"msg": f"Hello, {current_tg_agent.tg_id}! This is a protected route."} + # Авторизация +SECRET_KEY = "supersecretkey" # Лучше вынести в .env +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_MINUTES = 60 -@app.post("/token", response_model=Token, tags=["partner-tg"]) -async def login_for_access_token(req: TokenRequest): - tg_id = req.tg_id - tg_agent = authenticate_tg_agent(AUTH_DB_ENGINE, tg_id) - if not tg_agent: +def create_access_token(data: dict, expires_delta: timedelta = None): + to_encode = data.copy() + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=15) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + return encoded_jwt + +@app.post("/token", response_model=Token, tags=["bff"]) +def login_account_for_access_token( + login: str = Body(...), + password: str = Body(...), + db: Session = Depends(get_db) +): + account = get_account_by_login(db, login) + if not account or not verify_password(password, account.password_hash): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, - detail="Incorrect tg_id", + detail="Incorrect login or password", headers={"WWW-Authenticate": "Bearer"}, ) - access_token = f"session_for_{tg_agent.tg_id}" + access_token = create_access_token( + data={"sub": account.login}, + expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + ) return Token(access_token=access_token, token_type="bearer") @@ -474,3 +491,13 @@ def tg_auth(hash: str = Body(..., embed=True), db: Session = Depends(get_db)): if not tg_agent: raise HTTPException(status_code=401, detail="Hash not found") return {"msg": "Auth success", "tg_id": tg_agent.tg_id} + +# --- Новый функционал для Account --- +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +def verify_password(plain_password, hashed_password): + return pwd_context.verify(plain_password, hashed_password) + +def get_account_by_login(db: Session, login: str) -> Optional[Account]: + statement = select(Account).where(Account.login == login) + return db.exec(statement).first()