瀏覽代碼

Merge pull request #7 from shinbay-almaz/Madi

Added authentication
main
Madiwka 1 年之前
committed by GitHub
父節點
當前提交
60bb60e6fd
沒有發現已知的金鑰在資料庫的簽署中 GPG 金鑰 ID: 4AEE18F83AFDEB23
共有 14 個檔案被更改,包括 160 行新增31 行删除
  1. +2
    -1
      app/apis/base.py
  2. +60
    -0
      app/apis/v1/route_auth.py
  3. +13
    -4
      app/apis/v1/route_user.py
  4. +22
    -3
      app/apis/v1/route_vehicle.py
  5. +16
    -0
      app/core/auth.py
  6. +12
    -9
      app/core/config.py
  7. +0
    -2
      app/db/base.py
  8. +1
    -2
      app/db/models/user.py
  9. +0
    -5
      app/db/models/vehicle.py
  10. +10
    -0
      app/db/repository/user.py
  11. +14
    -2
      app/db/repository/vehicle.py
  12. +6
    -0
      app/schemas/token.py
  13. +1
    -2
      app/schemas/vehicle.py
  14. +3
    -1
      requirements.txt

+ 2
- 1
app/apis/base.py 查看文件

@@ -1,8 +1,9 @@
# Base API router -- collecting all APIs here to not clutter main.py
from fastapi import APIRouter

from apis.v1 import route_user, route_vehicle
from apis.v1 import route_user, route_vehicle, route_auth

api_router = APIRouter()
api_router.include_router(route_user.router, prefix="/user", tags=["users"])
api_router.include_router(route_vehicle.router, prefix="/vehicle", tags=["vehicles"])
api_router.include_router(route_auth.router, prefix="", tags=["auth"])

+ 60
- 0
app/apis/v1/route_auth.py 查看文件

@@ -0,0 +1,60 @@
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi import Depends, APIRouter
from sqlalchemy.orm import Session
from fastapi import status, HTTPException
from typing import Annotated
from db.session import get_db
from core.hashing import Hasher
from core.config import settings
from jose import JWTError, jwt
from schemas.token import Token
from db.repository.user import get_user_by_email, get_user_by_phone
from core.auth import create_access_token

router = APIRouter()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")


def authenticate_user(login: str, password: str, db: Session):
print("Trying to auth...")
user = None
if ("@" in login):
user = get_user_by_email(email=login, db=db)
elif ("+" in login):
user = get_user_by_phone(phone=login, db=db)
else:
return False
if not user:
return False
if not Hasher.verify_password(password, user.HashedPassword):
return False
return user


def get_current_user(token: Annotated[str, Depends(oauth2_scheme)], db: Annotated[Session, Depends(get_db)]):
print("Getting current user...")
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")
except JWTError:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")
if ("@" in username):
user = get_user_by_email(email=username, db=db)
elif ("+" in username):
user = get_user_by_phone(phone=username, db=db)
else:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials")
return user


@router.post("/token", response_model=Token)
def access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
print("Getting token...")
user = authenticate_user(form_data.username, form_data.password, db)
print(user)
if not user:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid username or password")
access_token = create_access_token(data={"sub": user.Email})
return {"access_token": access_token, "token_type": "bearer"}

+ 13
- 4
app/apis/v1/route_user.py 查看文件

@@ -2,8 +2,9 @@
from fastapi import APIRouter, HTTPException, status
from sqlalchemy.orm import Session
from fastapi import Depends
from typing import List

from typing import List, Annotated
from apis.v1.route_auth import get_current_user
from db.models.user import User
from schemas.user import UserCreate, ShowUser
from db.session import get_db
from db.repository.user import create_new_user, list_users, get_user_by_id
@@ -13,20 +14,28 @@ router = APIRouter()


@router.post("/", response_model=ShowUser, status_code=status.HTTP_201_CREATED)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
def create_user(user: UserCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
user = create_new_user(user=user, db=db)
return user


@router.get("/", response_model=List[ShowUser], status_code=status.HTTP_200_OK)
def get_all_users(db: Session = Depends(get_db), role: str = None):
if role == None:
if role is None:
users = list_users(db=db)
return users
users = list_users(db=db, role=role)
return users


@router.get("/me", response_model=ShowUser, status_code=status.HTTP_200_OK)
def get_user_me(current_user: Annotated[User, Depends(get_current_user)], db: Annotated[Session, Depends(get_db)]):
print("Getting current user...")
return current_user


@router.get("/{user_id}", response_model=ShowUser, status_code=status.HTTP_200_OK)
def get_user(user_id: int, db: Session = Depends(get_db)):
user = get_user_by_id(user_id=user_id, db=db)


+ 22
- 3
app/apis/v1/route_vehicle.py 查看文件

@@ -10,13 +10,18 @@ from db.repository.vehicle import (
list_vehicles,
get_vehicle_by_id,
replace_vehicle_data,
delete_vehicle_data,
)
from db.models.user import User
from apis.v1.route_auth import get_current_user

router = APIRouter()


@router.post("/", response_model=OutputVehicle, status_code=status.HTTP_201_CREATED)
async def create_vehicle(vehicle: CreateVehicle, db: Session = Depends(get_db)):
async def create_vehicle(vehicle: CreateVehicle, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
vehicle = create_new_vehicle(vehicle=vehicle, db=db)
return vehicle

@@ -47,7 +52,9 @@ async def create_vehicle(vehicle: CreateVehicle, db: Session = Depends(get_db)):
response_model=OutputVehicle,
status_code=status.HTTP_200_OK,
)
async def assign_driver(vehicle_id: int, driver_id: int, db: Session = Depends(get_db)):
async def assign_driver(vehicle_id: int, driver_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
vehicle = assign_vehicle_driver(vehicle_id=vehicle_id, driver_id=driver_id, db=db)
if vehicle == "nodriver":
raise HTTPException(
@@ -85,11 +92,23 @@ async def get_vehicle(vehicle_id: int, db: Session = Depends(get_db)):
"/{vehicle_id}", response_model=OutputVehicle, status_code=status.HTTP_200_OK
)
def update_vehicle(
vehicle_id: int, vehicle: UpdateVehicle, db: Session = Depends(get_db)
vehicle_id: int, vehicle: UpdateVehicle, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)
):
if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
vehicleRes = replace_vehicle_data(id=vehicle_id, vehicle=vehicle, db=db)
if vehicleRes == "vehicleNotFound":
raise HTTPException(status_code=404, detail="Vehicle not found")
elif vehicleRes == "badreq":
raise HTTPException(status_code=502, detail="Bad request")
return vehicleRes


@router.delete("/{vehicle_id}", status_code=status.HTTP_200_OK)
def delete_vehicle(vehicle_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
if current_user.Role != "Admin":
raise HTTPException(status_code=403, detail="You are not authorized to perform this action")
result = delete_vehicle_data(id=vehicle_id, db=db)
if result == "vehicleNotFound":
raise HTTPException(status_code=404, detail="Vehicle not found")
return {"msg": "Vehicle deleted successfully"}

+ 16
- 0
app/core/auth.py 查看文件

@@ -0,0 +1,16 @@
from datetime import datetime, timedelta
from typing import Optional
from jose import jwt

from core.config import settings


def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt

+ 12
- 9
app/core/config.py 查看文件

@@ -1,12 +1,15 @@
class Settings:
PROJECT_NAME:str = "VMS"
PROJECT_VERSION:str = "1.0.0"
POSTGRES_USER : str = "VMSBase"
PROJECT_NAME: str = "VMS"
PROJECT_VERSION: str = "1.0.0"
POSTGRES_USER: str = "VMSBase"
POSTGRES_PASSWORD = "VMSBasePass"
POSTGRES_SERVER : str = "localhost"
POSTGRES_PORT : str = "5432"
POSTGRES_DB : str = "VMSData"
POSTGRES_SERVER: str = "localhost"
POSTGRES_PORT: str = "5432"
POSTGRES_DB: str = "VMSData"
DATABASE_URL = f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_SERVER}:{POSTGRES_PORT}/{POSTGRES_DB}"
settings = Settings()
ACCESS_TOKEN_EXPIRE: int = 30
SECRET_KEY: str = "tH357aC6oA7ofCaN3yTffYkRh"
ALGORITHM: str = "HS256"


settings = Settings()

+ 0
- 2
app/db/base.py 查看文件

@@ -2,5 +2,3 @@
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
from db.models.user import User
from db.models.vehicle import Vehicle

+ 1
- 2
app/db/models/user.py 查看文件

@@ -1,6 +1,5 @@
# PostgreSQL table model for users
from sqlalchemy import Column, Integer, String, DateTime, Boolean, URL
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, DateTime
from db.base import Base




+ 0
- 5
app/db/models/vehicle.py 查看文件

@@ -3,13 +3,8 @@ from sqlalchemy import (
Column,
Integer,
String,
DateTime,
Boolean,
URL,
ARRAY,
ForeignKey,
)
from sqlalchemy.orm import relationship
from db.base import Base




+ 10
- 0
app/db/repository/user.py 查看文件

@@ -28,6 +28,16 @@ def get_user_by_id(user_id: int, db: Session):
return user


def get_user_by_email(email: str, db: Session):
user = db.query(User).filter(User.Email == email).first()
return user


def get_user_by_phone(phone: str, db: Session):
user = db.query(User).filter(User.ContactNumber == phone).first()
return user


def verify_driver_exists(driver_id: int, db: Session):
driver = db.query(User).filter(User.Id == driver_id).first()
if not driver:


+ 14
- 2
app/db/repository/vehicle.py 查看文件

@@ -1,5 +1,5 @@
from sqlalchemy.orm import Session
from schemas.vehicle import CreateVehicle, OutputVehicle, UpdateVehicle
from schemas.vehicle import CreateVehicle, UpdateVehicle
from db.models.vehicle import Vehicle
from db.repository.user import verify_driver_exists

@@ -27,7 +27,9 @@ def assign_vehicle_driver(vehicle_id: int, driver_id: int, db: Session):
return "alreadyassigned"
if verify_driver_exists(driver_id=driver_id, db=db):
print(vehicle.AssignedDriverIds)
vehicledb.update({"AssignedDriverIds": vehicle.AssignedDriverIds + [driver_id]})
vehicledb.update(
{"AssignedDriverIds": vehicle.AssignedDriverIds + [driver_id]}
)
print(vehicle.AssignedDriverIds)
db.add(vehicle)
db.commit()
@@ -65,3 +67,13 @@ def replace_vehicle_data(id: int, vehicle: UpdateVehicle, db: Session):
db.add(vehicle_object)
db.commit()
return vehicle_object


def delete_vehicle_data(id: int, db: Session):
vehicle_db = db.query(Vehicle).filter(Vehicle.Id == id)
vehicle_object = vehicle_db.first()
if not vehicle_object:
return "vehiclenotfound"
db.delete(vehicle_object)
db.commit()
return vehicle_object

+ 6
- 0
app/schemas/token.py 查看文件

@@ -0,0 +1,6 @@
from pydantic import BaseModel


class Token(BaseModel):
access_token: str
token_type: str

+ 1
- 2
app/schemas/vehicle.py 查看文件

@@ -1,6 +1,5 @@
from typing import Optional
from pydantic import BaseModel, root_validator
from datetime import datetime
from pydantic import BaseModel


class CreateVehicle(BaseModel):


+ 3
- 1
requirements.txt 查看文件

@@ -3,4 +3,6 @@ pydantic
sqlalchemy
psycopg2
alembic==1.12.0
passlib
passlib
python-jose==3.3.0
python-multipart==0.0.6

Loading…
取消
儲存