Add authorization

This commit is contained in:
2024-05-14 20:55:35 +04:00
parent 0565efdd15
commit 090897a11f
15 changed files with 255 additions and 24 deletions

View File

@@ -1,3 +1,4 @@
from .games import games_router as games_router
from .files import files_router as files_router
from .startup import startup_router as startup_router
from .auth import auth_router as auth_router

115
routes/auth.py Normal file
View File

@@ -0,0 +1,115 @@
from typing import Annotated, Any
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from passlib.context import CryptContext
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Depends, status, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel
from jose import JWTError, jwt
import database as db
from env import Env
SECRET_KEY = Env.get_strict("JWT_SECRET_KEY", str)
ACCESS_TOKEN_EXPIRE_MINUTES = \
Env.get_strict("JWT_ACCESS_TOKEN_EXPIRE_MINUTES", int)
crypt = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth")
auth_router = APIRouter(prefix="/auth", tags=["Auth"])
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: str
email: str
def check_password(password, hash): return crypt.verify(password, hash)
def get_hash(password): return crypt.hash(password)
async def get_user(token: str = Depends(oauth2_scheme),
db_session: AsyncSession = Depends(db.get_session)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY)
token_data = TokenData(**payload)
except Exception:
raise credentials_exception
user = await db.get_user(db_session, token_data.username)
if user is None:
raise credentials_exception
return user
def create_token(user: db.User):
access_token_expires = \
timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
expire = datetime.now(timezone.utc) + access_token_expires
to_encode = {
"username": user.name,
"email": user.email,
"expire": str(expire)
}
encoded_jwt = jwt.encode(to_encode, SECRET_KEY)
return Token(access_token=encoded_jwt, token_type="bearer")
@auth_router.post("/registration")
async def registration_user(
user_data: db.UserCreate,
db_session: AsyncSession = Depends(db.get_session)
) -> Token:
if (not await db.check_email(db_session, user_data.email)):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="This email is occupied by another user",
headers={"WWW-Authenticate": "Bearer"},
)
elif (await db.get_user(db_session, user_data.name) is not None):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User with the same name already exists",
headers={"WWW-Authenticate": "Bearer"},
)
else:
user = await db.add_user(db_session, user_data,
get_hash(user_data.password))
return create_token(user)
@auth_router.post("")
async def login_user(
auth_data: OAuth2PasswordRequestForm = Depends(),
db_session: AsyncSession = Depends(db.get_session)
) -> Token:
user = await db.get_user(db_session, auth_data.username)
if (user is None):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not found",
headers={"WWW-Authenticate": "Bearer"},
)
if (not check_password(auth_data.password, user.hash_of_password)):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect password",
headers={"WWW-Authenticate": "Bearer"},
)
return create_token(user)
@auth_router.get("/me", response_model=db.User)
async def read_me(user: db.User = Depends(get_user)):
return user

View File

@@ -3,15 +3,23 @@ from fastapi import APIRouter, Depends
import database as db
from file_handler import *
from routes.auth import get_user
games_router = APIRouter(prefix="/games", tags=["Games"])
@games_router.get("/", response_model=list[db.Game])
@games_router.get("", response_model=list[db.Game])
async def get_games(db_session: AsyncSession = Depends(db.get_session)):
return await db.get_games(db_session)
@games_router.post("", response_model=db.Game)
async def add_game(game: db.GameCreate,
user: db.User = Depends(get_user),
db_session: AsyncSession = Depends(db.get_session)):
return await db.add_game(db_session, game, user.id)
@games_router.get("/cards", response_model=list[db.GameCard])
async def get_games_cards(db_session: AsyncSession = Depends(db.get_session)):
return await db.get_games(db_session)
@@ -29,8 +37,7 @@ async def edit_game(game_id: int,
return await db.edit_game(db_session, game_id, game)
@games_router.post("/", response_model=db.Game)
async def add_game(game: db.GameCreate,
user_id: int,
db_session: AsyncSession = Depends(db.get_session)):
return await db.add_game(db_session, game, user_id)
@games_router.delete("/{game_id}", response_model=db.Game)
async def delete_game(game_id: int,
db_session: AsyncSession = Depends(db.get_session)):
return await db.delete_game(db_session, game_id)

View File

@@ -1,11 +1,12 @@
from fastapi import APIRouter
from pathlib import Path
from env import Env
startup_router = APIRouter()
@startup_router.on_event("startup")
def startup():
def create_folders():
need_paths = [
Path() / "content" / "images" / "cover" / "full_size",
Path() / "content" / "images" / "cover" / "preview",
@@ -15,3 +16,8 @@ def startup():
]
for path in need_paths:
path.mkdir(parents=True, exist_ok=True)
@startup_router.on_event("startup")
def startup():
create_folders()