Skip to content

Commit

Permalink
After module 8: Authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
codesensei-courses committed Apr 18, 2022
1 parent 29f2eb1 commit a3dd928
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 10 deletions.
Binary file modified carsharing.db
Binary file not shown.
15 changes: 9 additions & 6 deletions carsharing.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import uvicorn
from fastapi import FastAPI, Request
from fastapi import FastAPI
from fastapi import Request
from fastapi.middleware.cors import CORSMiddleware
from sqlmodel import SQLModel
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from sqlmodel import SQLModel, Session, select
from starlette import status
from starlette.responses import JSONResponse
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY

from db import engine
from routers import cars, web
from db import engine, get_session
from routers import cars, web, auth
from routers.cars import BadTripException
from schemas import UserOutput, User

app = FastAPI(title="Car Sharing")
app.include_router(web.router)
app.include_router(cars.router)

app.include_router(auth.router)

origins = [
"http://localhost:8000",
Expand Down Expand Up @@ -50,4 +54,3 @@ async def add_cars_cookie(request: Request, call_next):

if __name__ == "__main__":
uvicorn.run("carsharing:app", reload=True)

36 changes: 36 additions & 0 deletions create_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""
create_user.py
-------------
A convenience script to create a user.
"""

from getpass import getpass

from sqlmodel import SQLModel, Session, create_engine

from schemas import User


engine = create_engine(
"sqlite:///carsharing.db",
connect_args={"check_same_thread": False}, # Needed for SQLite
echo=True # Log generated SQL
)


if __name__ == "__main__":
print("Creating tables (if necessary)")
SQLModel.metadata.create_all(engine)

print("--------")

print("This script will create a user and save it in the database.")

username = input("Please enter username\n")
pwd = getpass("Please enter password\n")

with Session(engine) as session:
user = User(username=username)
user.set_password(pwd)
session.add(user)
session.commit()
36 changes: 36 additions & 0 deletions routers/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from fastapi import Depends, HTTPException, APIRouter
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlmodel import Session, select
from starlette import status

from db import get_session
from schemas import UserOutput, User

URL_PREFIX="/auth"
router = APIRouter(prefix=URL_PREFIX)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{URL_PREFIX}/token")


def get_current_user(token: str = Depends(oauth2_scheme),
session: Session = Depends(get_session)) -> UserOutput:
query = select(User).where(User.username == token)
user = session.exec(query).first()
if user:
return UserOutput.from_orm(user)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Username or password incorrect",
headers={"WWW-Authenticate": "Bearer"},
)


@router.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends(),
session: Session = Depends(get_session)):
query = select(User).where(User.username == form_data.username)
user = session.exec(query).first()
if user and user.verify_password(form_data.password):
return {"access_token": user.username, "token_type": "bearer"}
else:
raise HTTPException(status_code=400, detail="Incorrect username or password")
8 changes: 5 additions & 3 deletions routers/cars.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from fastapi import Depends, HTTPException, APIRouter
from sqlmodel import Session, select

from routers.auth import get_current_user
from db import get_session
from schemas import Car, CarOutput, CarInput, Trip, TripInput

from schemas import Car, CarOutput, CarInput, Trip, TripInput, User

router = APIRouter(prefix="/api/cars")

Expand All @@ -29,7 +29,9 @@ def car_by_id(id: int, session: Session = Depends(get_session)) -> Car:


@router.post("/", response_model=Car)
def add_car(car_input: CarInput, session: Session = Depends(get_session)) -> Car:
def add_car(car_input: CarInput,
session: Session = Depends(get_session),
user: User = Depends(get_current_user)) -> Car:
new_car = Car.from_orm(car_input)
session.add(new_car)
session.commit()
Expand Down
23 changes: 22 additions & 1 deletion schemas.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
from sqlmodel import SQLModel, Field, Relationship
from sqlmodel import SQLModel, Field, Relationship, Column, VARCHAR
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"])

class UserOutput(SQLModel):
id: int
username: str


class User(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
username: str = Field(sa_column=Column("username", VARCHAR, unique=True, index=True))
password_hash: str = ""

def set_password(self, password):
"""Setting the passwords actually sets password_hash."""
self.password_hash = pwd_context.hash(password)

def verify_password(self, password):
"""Verify given password by hashing and comparing to password_hash."""
return pwd_context.verify(password, self.password_hash)


class TripInput(SQLModel):
Expand Down

0 comments on commit a3dd928

Please sign in to comment.