- Updated architecture constraints documentation to include detailed sections on technical, organizational, regulatory, environmental, and performance constraints. - Created separate markdown files for each type of constraint for better organization and clarity. - Revised the architecture scope section to provide a clearer overview of the system's key areas. - Enhanced the solution strategy documentation with detailed explanations of the client-server architecture, technology choices, trade-offs, and future considerations. - Added comprehensive descriptions of backend and frontend components, middleware, and utilities in the architecture documentation. - Migrated UI, templates, and styling notes to a dedicated section for better structure. - Updated requirements.txt to include missing dependencies. - Refactored user authentication logic in the users.py and security.py files to improve code organization and maintainability, including the integration of OAuth2 password bearer token handling.
102 lines
4.1 KiB
Python
102 lines
4.1 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
|
|
from config.database import get_db
|
|
from models.user import User
|
|
from services.security import get_password_hash, verify_password, create_access_token, SECRET_KEY, ALGORITHM, get_current_user, oauth2_scheme
|
|
from jose import jwt, JWTError
|
|
from schemas.user import UserCreate, UserInDB, UserLogin, UserUpdate, PasswordResetRequest, PasswordReset, Token
|
|
|
|
router = APIRouter(prefix="/users", tags=["users"])
|
|
|
|
|
|
@router.post("/register", response_model=UserInDB, status_code=status.HTTP_201_CREATED)
|
|
async def register_user(user: UserCreate, db: Session = Depends(get_db)):
|
|
db_user = db.query(User).filter(User.username == user.username).first()
|
|
if db_user:
|
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Username already registered")
|
|
db_user = db.query(User).filter(User.email == user.email).first()
|
|
if db_user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered")
|
|
|
|
# Get or create default role
|
|
from models.role import Role
|
|
default_role = db.query(Role).filter(Role.name == "user").first()
|
|
if not default_role:
|
|
default_role = Role(name="user")
|
|
db.add(default_role)
|
|
db.commit()
|
|
db.refresh(default_role)
|
|
|
|
new_user = User(username=user.username, email=user.email,
|
|
role_id=default_role.id)
|
|
new_user.set_password(user.password)
|
|
db.add(new_user)
|
|
db.commit()
|
|
db.refresh(new_user)
|
|
return new_user
|
|
|
|
|
|
@router.post("/login")
|
|
async def login_user(user: UserLogin, db: Session = Depends(get_db)):
|
|
db_user = db.query(User).filter(User.username == user.username).first()
|
|
if not db_user or not db_user.check_password(user.password):
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Incorrect username or password")
|
|
access_token = create_access_token(subject=db_user.username)
|
|
return {"access_token": access_token, "token_type": "bearer"}
|
|
|
|
|
|
@router.get("/me")
|
|
async def read_users_me(current_user: User = Depends(get_current_user)):
|
|
return current_user
|
|
|
|
|
|
@router.put("/me", response_model=UserInDB)
|
|
async def update_user_me(user_update: UserUpdate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
|
if user_update.username and user_update.username != current_user.username:
|
|
existing_user = db.query(User).filter(
|
|
User.username == user_update.username).first()
|
|
if existing_user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail="Username already taken")
|
|
current_user.username = user_update.username
|
|
|
|
if user_update.email and user_update.email != current_user.email:
|
|
existing_user = db.query(User).filter(
|
|
User.email == user_update.email).first()
|
|
if existing_user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered")
|
|
current_user.email = user_update.email
|
|
|
|
if user_update.password:
|
|
current_user.set_password(user_update.password)
|
|
|
|
db.add(current_user)
|
|
db.commit()
|
|
db.refresh(current_user)
|
|
return current_user
|
|
|
|
|
|
@router.post("/forgot-password")
|
|
async def forgot_password(request: PasswordResetRequest):
|
|
# In a real application, this would send an email with a reset token
|
|
return {"message": "Password reset email sent (not really)"}
|
|
|
|
|
|
@router.post("/reset-password")
|
|
async def reset_password(request: PasswordReset, db: Session = Depends(get_db)):
|
|
# In a real application, the token would be verified
|
|
user = db.query(User).filter(User.username ==
|
|
request.token).first() # Use token as username for test
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid token or user")
|
|
user.set_password(request.new_password)
|
|
db.add(user)
|
|
db.commit()
|
|
return {"message": "Password has been reset successfully"}
|