Update all for Postgres and new techniques

This commit is contained in:
Sebastián Ramírez
2019-02-23 18:44:29 +04:00
parent 1b4d244033
commit 6fdba19639
72 changed files with 793 additions and 1316 deletions

View File

@@ -1,12 +1,10 @@
from fastapi import APIRouter
from app.api.api_v1.endpoints.role import router as roles_router
from app.api.api_v1.endpoints.token import router as token_router
from app.api.api_v1.endpoints.user import router as user_router
from app.api.api_v1.endpoints.utils import router as utils_router
api_router = APIRouter()
api_router.include_router(roles_router)
api_router.include_router(token_router)
api_router.include_router(user_router)
api_router.include_router(utils_router)

View File

@@ -1,25 +0,0 @@
from fastapi import APIRouter, Depends
from starlette.exceptions import HTTPException
from app.core.jwt import get_current_user
from app.crud.user import check_if_user_is_active, check_if_user_is_superuser
from app.crud.utils import ensure_enums_to_strs
from app.models.role import RoleEnum, Roles
from app.models.user import UserInDB
router = APIRouter()
@router.get("/roles/", response_model=Roles)
def route_roles_get(current_user: UserInDB = Depends(get_current_user)):
"""
Retrieve roles
"""
if not check_if_user_is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
elif not (check_if_user_is_superuser(current_user)):
raise HTTPException(
status_code=400, detail="The current user does not have enogh privileges"
)
roles = ensure_enums_to_strs(RoleEnum)
return {"roles": roles}

View File

@@ -1,22 +1,19 @@
from datetime import timedelta
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from starlette.exceptions import HTTPException
from sqlalchemy.orm import Session
from app.api.utils.db import get_db
from app.api.utils.security import get_current_user
from app.core import config
from app.core.jwt import create_access_token, get_current_user
from app.crud.user import (
authenticate_user,
check_if_user_is_active,
check_if_user_is_superuser,
get_user,
update_user,
)
from app.db.database import get_default_bucket
from app.core.jwt import create_access_token
from app.core.security import get_password_hash
from app.crud import user as crud_user
from app.db_models.user import User as DBUser
from app.models.msg import Msg
from app.models.token import Token
from app.models.user import User, UserInDB, UserInUpdate
from app.models.user import User
from app.utils import (
generate_password_reset_token,
send_reset_password_email,
@@ -27,70 +24,73 @@ router = APIRouter()
@router.post("/login/access-token", response_model=Token, tags=["login"])
def route_login_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
def login_access_token(
db: Session = Depends(get_db), form_data: OAuth2PasswordRequestForm = Depends()
):
"""
OAuth2 compatible token login, get an access token for future requests
"""
bucket = get_default_bucket()
user = authenticate_user(bucket, form_data.username, form_data.password)
user = crud_user.authenticate(
db, email=form_data.username, password=form_data.password
)
if not user:
raise HTTPException(status_code=400, detail="Incorrect email or password")
elif not check_if_user_is_active(user):
elif not crud_user.is_active(user):
raise HTTPException(status_code=400, detail="Inactive user")
access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES)
return {
"access_token": create_access_token(
data={"username": form_data.username}, expires_delta=access_token_expires
data={"user_id": user.id}, expires_delta=access_token_expires
),
"token_type": "bearer",
}
@router.post("/login/test-token", tags=["login"], response_model=User)
def route_test_token(current_user: UserInDB = Depends(get_current_user)):
def test_token(current_user: DBUser = Depends(get_current_user)):
"""
Test access token
"""
return current_user
@router.post("/password-recovery/{username}", tags=["login"], response_model=Msg)
def route_recover_password(username: str):
@router.post("/password-recovery/{email}", tags=["login"], response_model=Msg)
def recover_password(email: str, db: Session = Depends(get_db)):
"""
Password Recovery
"""
bucket = get_default_bucket()
user = get_user(bucket, username)
user = crud_user.get_by_email(db, 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(username)
password_reset_token = generate_password_reset_token(email=email)
send_reset_password_email(
email_to=user.email, username=username, token=password_reset_token
email_to=user.email, email=email, token=password_reset_token
)
return {"msg": "Password recovery email sent"}
@router.post("/reset-password/", tags=["login"], response_model=Msg)
def route_reset_password(token: str, new_password: str):
def reset_password(token: str, new_password: str, db: Session = Depends(get_db)):
"""
Reset password
"""
username = verify_password_reset_token(token)
if not username:
email = verify_password_reset_token(token)
if not email:
raise HTTPException(status_code=400, detail="Invalid token")
bucket = get_default_bucket()
user = get_user(bucket, username)
user = crud_user.get_by_email(db, email=email)
if not user:
raise HTTPException(
status_code=404,
detail="The user with this username does not exist in the system.",
)
elif not check_if_user_is_active(user):
elif not crud_user.is_active(user):
raise HTTPException(status_code=400, detail="Inactive user")
user_in = UserInUpdate(name=username, password=new_password)
user = update_user(bucket, user_in)
hashed_password = get_password_hash(new_password)
user.hashed_password = hashed_password
db.add(user)
db.commit()
return {"msg": "Password updated successfully"}

View File

@@ -1,21 +1,15 @@
from typing import List
from fastapi import APIRouter, Body, Depends
from fastapi import APIRouter, Body, Depends, HTTPException
from fastapi.encoders import jsonable_encoder
from pydantic.types import EmailStr
from starlette.exceptions import HTTPException
from sqlalchemy.orm import Session
from app.api.utils.db import get_db
from app.api.utils.security import get_current_user
from app.core import config
from app.core.jwt import get_current_user
from app.crud.user import (
check_if_user_is_active,
check_if_user_is_superuser,
get_user,
get_users,
search_users,
update_user,
upsert_user,
)
from app.db.database import get_default_bucket
from app.crud import user as crud_user
from app.db_models.user import User as DBUser
from app.models.user import User, UserInCreate, UserInDB, UserInUpdate
from app.utils import send_new_account_email
@@ -23,116 +17,99 @@ router = APIRouter()
@router.get("/users/", tags=["users"], response_model=List[User])
def route_users_get(
skip: int = 0, limit: int = 100, current_user: UserInDB = Depends(get_current_user)
def read_users(
db: Session = Depends(get_db),
skip: int = 0,
limit: int = 100,
current_user: DBUser = Depends(get_current_user),
):
"""
Retrieve users
"""
if not check_if_user_is_active(current_user):
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
elif not check_if_user_is_superuser(current_user):
elif not crud_user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
bucket = get_default_bucket()
users = get_users(bucket, skip=skip, limit=limit)
return users
@router.get("/users/search/", tags=["users"], response_model=List[User])
def route_search_users(
q: str,
skip: int = 0,
limit: int = 100,
current_user: UserInDB = Depends(get_current_user),
):
"""
Search users, use Bleve Query String syntax: http://blevesearch.com/docs/Query-String-Query/
For typeahead sufix with `*`. For example, a query with: `email:johnd*` will match users with
email `johndoe@example.com`, `johndid@example.net`, etc.
"""
if not check_if_user_is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
elif not check_if_user_is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
bucket = get_default_bucket()
users = search_users(bucket=bucket, query_string=q, skip=skip, limit=limit)
users = crud_user.get_multi(db, skip=skip, limit=limit)
return users
@router.post("/users/", tags=["users"], response_model=User)
def route_users_post(
*, user_in: UserInCreate, current_user: UserInDB = Depends(get_current_user)
def create_user(
*,
db: Session = Depends(get_db),
user_in: UserInCreate,
current_user: DBUser = Depends(get_current_user),
):
"""
Create new user
"""
if not check_if_user_is_active(current_user):
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
elif not check_if_user_is_superuser(current_user):
elif not crud_user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
bucket = get_default_bucket()
user = get_user(bucket, user_in.username)
user = crud_user.get_by_email(db, email=user_in.email)
if user:
raise HTTPException(
status_code=400,
detail="The user with this username already exists in the system.",
)
user = upsert_user(bucket, user_in, persist_to=1)
user = crud_user.create(db, user_in=user_in)
if config.EMAILS_ENABLED and user_in.email:
send_new_account_email(
email_to=user_in.email, username=user_in.username, password=user_in.password
email_to=user_in.email, username=user_in.email, password=user_in.password
)
return user
@router.put("/users/me", tags=["users"], response_model=User)
def route_users_me_put(
def update_user_me(
*,
password: str = None,
full_name: str = None,
email: EmailStr = None,
current_user: UserInDB = Depends(get_current_user),
db: Session = Depends(get_db),
password: str = Body(None),
full_name: str = Body(None),
email: EmailStr = Body(None),
current_user: DBUser = Depends(get_current_user),
):
"""
Update own user
"""
if not check_if_user_is_active(current_user):
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
user_in = UserInUpdate(**current_user.dict())
current_user_data = jsonable_encoder(current_user)
user_in = UserInUpdate(**current_user_data)
if password is not None:
user_in.password = password
if full_name is not None:
user_in.full_name = full_name
if email is not None:
user_in.email = email
bucket = get_default_bucket()
user = update_user(bucket, user_in)
user = crud_user.update(db, user=current_user, user_in=user_in)
return user
@router.get("/users/me", tags=["users"], response_model=User)
def route_users_me_get(current_user: UserInDB = Depends(get_current_user)):
def read_user_me(
db: Session = Depends(get_db), current_user: DBUser = Depends(get_current_user)
):
"""
Get current user
"""
if not check_if_user_is_active(current_user):
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@router.post("/users/open", tags=["users"], response_model=User)
def route_users_post_open(
def create_user_open(
*,
username: str = Body(...),
db: Session = Depends(get_db),
password: str = Body(...),
email: EmailStr = Body(None),
email: EmailStr = Body(...),
full_name: str = Body(None),
):
"""
@@ -143,63 +120,61 @@ def route_users_post_open(
status_code=403,
detail="Open user resgistration is forbidden on this server",
)
bucket = get_default_bucket()
user = get_user(bucket, username)
user = crud_user.get_by_email(db, email=email)
if user:
raise HTTPException(
status_code=400,
detail="The user with this username already exists in the system",
)
user_in = UserInCreate(
username=username, password=password, email=email, full_name=full_name
)
user = upsert_user(bucket, user_in, persist_to=1)
user_in = UserInCreate(password=password, email=email, full_name=full_name)
user = crud_user.create(db, user_in=user_in)
return user
@router.get("/users/{username}", tags=["users"], response_model=User)
def route_users_id_get(
username: str, current_user: UserInDB = Depends(get_current_user)
@router.get("/users/{user_id}", tags=["users"], response_model=User)
def read_user_by_id(
user_id: int,
current_user: DBUser = Depends(get_current_user),
db: Session = Depends(get_db),
):
"""
Get a specific user by username (email)
"""
if not check_if_user_is_active(current_user):
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
bucket = get_default_bucket()
user = get_user(bucket, username)
user = crud_user.get(db, user_id=user_id)
if user == current_user:
return user
if not check_if_user_is_superuser(current_user):
if not crud_user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
return user
@router.put("/users/{username}", tags=["users"], response_model=User)
def route_users_put(
@router.put("/users/{user_id}", tags=["users"], response_model=User)
def update_user(
*,
username: str,
db: Session = Depends(get_db),
user_id: int,
user_in: UserInUpdate,
current_user: UserInDB = Depends(get_current_user),
):
"""
Update a user
"""
if not check_if_user_is_active(current_user):
if not crud_user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
elif not check_if_user_is_superuser(current_user):
elif not crud_user.is_superuser(current_user):
raise HTTPException(
status_code=400, detail="The user doesn't have enough privileges"
)
bucket = get_default_bucket()
user = get_user(bucket, username)
user = crud_user.get(db, user_id=user_id)
if not user:
raise HTTPException(
status_code=404,
detail="The user with this username does not exist in the system",
)
user = update_user(bucket, user_in)
user = crud_user.update(db, user=user, user_in=user_in)
return user

View File

@@ -1,10 +1,9 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, HTTPException
from pydantic.types import EmailStr
from starlette.exceptions import HTTPException
from app.api.utils.security import get_current_user
from app.core.celery_app import celery_app
from app.core.jwt import get_current_user
from app.crud.user import check_if_user_is_superuser
from app.crud import user as crud_user
from app.models.msg import Msg
from app.models.user import UserInDB
from app.utils import send_test_email
@@ -13,24 +12,22 @@ router = APIRouter()
@router.post("/test-celery/", tags=["utils"], response_model=Msg, status_code=201)
def route_test_celery(msg: Msg, current_user: UserInDB = Depends(get_current_user)):
def test_celery(msg: Msg, current_user: UserInDB = Depends(get_current_user)):
"""
Test Celery worker
"""
if not check_if_user_is_superuser(current_user):
if not crud_user.is_superuser(current_user):
raise HTTPException(status_code=400, detail="Not a superuser")
celery_app.send_task("app.worker.test_celery", args=[msg.msg])
return {"msg": "Word received"}
@router.post("/test-email/", tags=["utils"], response_model=Msg, status_code=201)
def route_test_email(
email_to: EmailStr, current_user: UserInDB = Depends(get_current_user)
):
def test_email(email_to: EmailStr, current_user: UserInDB = Depends(get_current_user)):
"""
Test emails
"""
if not check_if_user_is_superuser(current_user):
if not crud_user.is_superuser(current_user):
raise HTTPException(status_code=400, detail="Not a superuser")
send_test_email(email_to=email_to)
return {"msg": "Test email sent"}

View File

@@ -0,0 +1,5 @@
from starlette.requests import Request
def get_db(request: Request):
return request.state.db

View File

@@ -0,0 +1,30 @@
import jwt
from fastapi import Depends, HTTPException, Security
from fastapi.security import OAuth2PasswordBearer
from jwt import PyJWTError
from sqlalchemy.orm import Session
from starlette.status import HTTP_403_FORBIDDEN
from app.api.utils.db import get_db
from app.core import config
from app.core.jwt import ALGORITHM
from app.crud import user as crud_user
from app.models.token import TokenPayload
reusable_oauth2 = OAuth2PasswordBearer(tokenUrl="/api/v1/login/access-token")
def get_current_user(
db: Session = Depends(get_db), token: str = Security(reusable_oauth2)
):
try:
payload = jwt.decode(token, config.SECRET_KEY, algorithms=[ALGORITHM])
token_data = TokenPayload(**payload)
except PyJWTError:
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
)
user = crud_user.get(db, user_id=token_data.user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user