Skip to content

Commit

Permalink
ADD: security and auth
Browse files Browse the repository at this point in the history
  • Loading branch information
mazzasaverio committed Apr 1, 2024
1 parent ca5d82a commit 39e2527
Show file tree
Hide file tree
Showing 10 changed files with 377 additions and 17 deletions.
11 changes: 8 additions & 3 deletions backend/app/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
from sqlmodel import Session

from app.core import security
from backend.app.core.config import settings
from app.core.db import engine
from app.models import TokenPayload, User
from app.core.config import settings, logger

from app.models.user_model import TokenPayload, User
from sqlmodel import Session, create_engine, select


reusable_oauth2 = OAuth2PasswordBearer(
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
)

engine = create_engine(str(settings.SYNC_DATABASE_URI))


def get_db() -> Generator[Session, None, None]:
with Session(engine) as session:
Expand Down Expand Up @@ -49,6 +53,7 @@ def get_current_user(session: SessionDep, token: TokenDep) -> User:


def get_current_active_superuser(current_user: CurrentUser) -> User:
logger.debug(f"################### current_user: {current_user}")
if not current_user.is_superuser:
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
Expand Down
124 changes: 124 additions & 0 deletions backend/app/api/routes/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from datetime import timedelta
from typing import Annotated, Any

from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import HTMLResponse
from fastapi.security import OAuth2PasswordRequestForm

from app import crud
from app.api.deps import CurrentUser, SessionDep, get_current_active_superuser
from app.core import security
from app.core.config import settings
from app.core.security import get_password_hash
from app.models import Message, NewPassword, Token, UserOut
from app.utils import (
generate_password_reset_token,
generate_reset_password_email,
send_email,
verify_password_reset_token,
)

router = APIRouter()


@router.post("/login/access-token")
def login_access_token(
session: SessionDep, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
) -> Token:
"""
OAuth2 compatible token login, get an access token for future requests
"""
user = crud.authenticate(
session=session, email=form_data.username, password=form_data.password
)
if not user:
raise HTTPException(status_code=400, detail="Incorrect email or password")
elif not user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
return Token(
access_token=security.create_access_token(
user.id, expires_delta=access_token_expires
)
)


@router.post("/login/test-token", response_model=UserOut)
def test_token(current_user: CurrentUser) -> Any:
"""
Test access token
"""
return current_user


@router.post("/password-recovery/{email}")
def recover_password(email: str, session: SessionDep) -> Message:
"""
Password Recovery
"""
user = crud.get_user_by_email(session=session, email=email)

if not user:
raise HTTPException(
status_code=404,
detail="The user with this email does not exist in the system.",
)
password_reset_token = generate_password_reset_token(email=email)
email_data = generate_reset_password_email(
email_to=user.email, email=email, token=password_reset_token
)
send_email(
email_to=user.email,
subject=email_data.subject,
html_content=email_data.html_content,
)
return Message(message="Password recovery email sent")


@router.post("/reset-password/")
def reset_password(session: SessionDep, body: NewPassword) -> Message:
"""
Reset password
"""
email = verify_password_reset_token(token=body.token)
if not email:
raise HTTPException(status_code=400, detail="Invalid token")
user = crud.get_user_by_email(session=session, email=email)
if not user:
raise HTTPException(
status_code=404,
detail="The user with this email does not exist in the system.",
)
elif not user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
hashed_password = get_password_hash(password=body.new_password)
user.hashed_password = hashed_password
session.add(user)
session.commit()
return Message(message="Password updated successfully")


@router.post(
"/password-recovery-html-content/{email}",
dependencies=[Depends(get_current_active_superuser)],
response_class=HTMLResponse,
)
def recover_password_html_content(email: str, session: SessionDep) -> Any:
"""
HTML Content for Password Recovery
"""
user = crud.get_user_by_email(session=session, email=email)

if not user:
raise HTTPException(
status_code=404,
detail="The user with this username does not exist in the system.",
)
password_reset_token = generate_password_reset_token(email=email)
email_data = generate_reset_password_email(
email_to=user.email, email=email, token=password_reset_token
)

return HTMLResponse(
content=email_data.html_content, headers={"subject:": email_data.subject}
)
17 changes: 11 additions & 6 deletions backend/app/api/routes/qa.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from app.core.db import SessionLocal
import os
import yaml
from fastapi import APIRouter


from backend.app.core.config import logger
from app.core.config import logger, settings


from operator import itemgetter
from typing import Annotated

from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
Expand All @@ -19,21 +18,27 @@
from langchain_core.runnables import RunnableParallel
from langchain_community.vectorstores.pgvector import PGVector
from langchain.memory import ConversationBufferMemory
from backend.app.core.config import settings

from langchain.prompts.prompt import PromptTemplate
from app.schemas.chat_schema import ChatBody
from fastapi import APIRouter, Depends
from app.api.deps import get_current_active_superuser

router = APIRouter()

config_path = os.path.join(os.path.dirname(__file__), "..", "..", "config.yml")
config_path = os.path.join(os.path.dirname(__file__), "..", "..", "config/chat.yml")
with open(config_path, "r") as config_file:
config = yaml.load(config_file, Loader=yaml.FullLoader)

chat_config = config.get("CHAT_CONFIG", None)


@router.post("/chat")
async def chat_action(request: ChatBody):
async def chat_action(
request: ChatBody,
jwt: Annotated[dict, Depends(get_current_active_superuser)],
):
logger.info(f"User JWT from request: {jwt}")

embeddings = OpenAIEmbeddings()

Expand Down
Loading

0 comments on commit 39e2527

Please sign in to comment.