Add updateable route for my user
This commit is contained in:
@@ -1,14 +1,5 @@
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from tortoise.contrib.pydantic import pydantic_model_creator
|
||||
|
||||
from modules.auth.models import Token
|
||||
|
||||
token_model = pydantic_model_creator(Token)
|
||||
|
||||
class register_model(BaseModel):
|
||||
email: EmailStr
|
||||
username: str
|
||||
name: str
|
||||
surname: str
|
||||
password: str
|
||||
validate_password: str
|
||||
token_model = pydantic_model_creator(Token)
|
||||
@@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from fastapi import HTTPException, status
|
||||
from pydantic import EmailStr
|
||||
import pytz
|
||||
from tortoise.models import Model
|
||||
@@ -41,9 +42,10 @@ class User(Model):
|
||||
def __str__(self) -> str:
|
||||
return f"{self.id} - {self.name} {self.surname}"
|
||||
|
||||
async def set_password(self, password: str) -> None:
|
||||
async def set_password(self, password: str) -> bool:
|
||||
self.password = crypt.hash(password)
|
||||
await self.save() # Make sure to save the model in DB
|
||||
return True
|
||||
|
||||
def check_against_password(self, password: str) -> bool:
|
||||
return crypt.verify(password, self.password)
|
||||
@@ -55,7 +57,7 @@ class User(Model):
|
||||
return False
|
||||
if new_password is not verify_new_password:
|
||||
return False
|
||||
await self.set_password(new_password)
|
||||
return await self.set_password(new_password)
|
||||
|
||||
async def delete(self, force: bool = False) -> None:
|
||||
if force:
|
||||
|
||||
@@ -6,7 +6,7 @@ from fastapi import HTTPException, status
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from modules.users.utils import get_current_active_user
|
||||
from modules.auth.schemas import register_model
|
||||
from modules.users.schemas import register_model, update_user_model
|
||||
from modules.users.models import User
|
||||
from modules.users.schemas import user_model
|
||||
|
||||
@@ -24,12 +24,13 @@ password_failed: str = "Password validation failed, please try again."
|
||||
@router.post("/", status_code=status.HTTP_201_CREATED, response_model=user_model)
|
||||
async def create_user(user: register_model):
|
||||
# Prevent existing users from reapplying for our system.
|
||||
existing_user: User | None = await User.filter(
|
||||
existing_user: User | None = await User.get_or_none(
|
||||
Q(email=user.email)
|
||||
& Q(username=user.username)
|
||||
& Q(name=user.name)
|
||||
& Q(surname=user.surname)
|
||||
).get_or_none()
|
||||
& Q(disabled=False)
|
||||
)
|
||||
|
||||
if existing_user is not None:
|
||||
raise HTTPException(
|
||||
@@ -52,6 +53,22 @@ async def create_user(user: register_model):
|
||||
)
|
||||
|
||||
|
||||
@router.put("/me", status_code=status.HTTP_204_NO_CONTENT)
|
||||
async def update_user(user: Annotated[User, Depends(get_current_active_user)],
|
||||
updated_user: update_user_model):
|
||||
if updated_user.email:
|
||||
user.email = updated_user.email
|
||||
if updated_user.name:
|
||||
user.name = updated_user.name
|
||||
if updated_user.surname:
|
||||
user.surname = updated_user.surname
|
||||
|
||||
if updated_user.old_password and updated_user.password and updated_user.validate_password:
|
||||
user.update_password(updated_user.old_password, updated_user.password, updated_user.validate_password)
|
||||
|
||||
await user.save()
|
||||
|
||||
|
||||
@router.get("/me", response_model=user_model)
|
||||
async def get_user(user: Annotated[User, Depends(get_current_active_user)]):
|
||||
return user
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from tortoise.contrib.pydantic import pydantic_model_creator
|
||||
|
||||
from modules.users.models import User
|
||||
|
||||
user_model = pydantic_model_creator(User, exclude=["password"])
|
||||
|
||||
class register_model(BaseModel):
|
||||
email: EmailStr
|
||||
username: str
|
||||
name: str
|
||||
surname: str
|
||||
password: str
|
||||
validate_password: str
|
||||
|
||||
|
||||
class update_user_model(BaseModel):
|
||||
email: EmailStr | None
|
||||
name: str | None
|
||||
surname: str | None
|
||||
old_password: str | None
|
||||
password: str | None
|
||||
validate_password: str | None
|
||||
|
||||
@@ -10,7 +10,7 @@ from config import settings
|
||||
|
||||
|
||||
async def get_user_from_token(
|
||||
token: Annotated[str, Depends(settings.OAUTH2_SCHEME)]
|
||||
token: Annotated[str, Depends(settings.OAUTH2_SCHEME)],
|
||||
) -> User | None:
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
@@ -28,7 +28,7 @@ async def get_user_from_token(
|
||||
except:
|
||||
raise credentials_exception
|
||||
|
||||
return await User.filter(Q(id=user_id)).first()
|
||||
return await User.filter(Q(id=user_id) & Q(disabled=False)).first()
|
||||
|
||||
|
||||
async def get_current_active_user(
|
||||
@@ -37,11 +37,6 @@ async def get_current_active_user(
|
||||
if user is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="User is not found or active",
|
||||
)
|
||||
if user.disabled:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="User is not found or active",
|
||||
detail="The requested token does not exist or you are not logged in.",
|
||||
)
|
||||
return user
|
||||
|
||||
@@ -40,7 +40,6 @@ class TestAccounts(Test):
|
||||
}
|
||||
|
||||
async def test_me_route(self, client: AsyncClient, create_user_with_org):
|
||||
# Ensure account is never available. Prevents account already being available.
|
||||
_, _, _, tokens = await create_user_with_org()
|
||||
|
||||
|
||||
@@ -60,4 +59,59 @@ class TestAccounts(Test):
|
||||
"name": "awesome",
|
||||
"surname": "user",
|
||||
"username": "user",
|
||||
}
|
||||
|
||||
async def test_update_me_route(self, client: AsyncClient, create_user_with_org):
|
||||
_, _, _, tokens = await create_user_with_org()
|
||||
|
||||
|
||||
account = await client.get(
|
||||
"https://localhost/api/v1/users/me",
|
||||
headers={"Authorization": f"Bearer {tokens.access_token}"},
|
||||
)
|
||||
|
||||
assert account.status_code == 200
|
||||
assert account.json() == {
|
||||
"created_at": ANY,
|
||||
"disabled": False,
|
||||
"disabled_at": None,
|
||||
"email": "user@localhost.com",
|
||||
"id": ANY,
|
||||
"modified_at": ANY,
|
||||
"name": "awesome",
|
||||
"surname": "user",
|
||||
"username": "user",
|
||||
}
|
||||
|
||||
account = await client.put(
|
||||
"https://localhost/api/v1/users/me",
|
||||
json={
|
||||
"email": None,
|
||||
"name": None,
|
||||
"surname": "bluey",
|
||||
"old_password": None,
|
||||
"password": None,
|
||||
"validate_password": None,
|
||||
},
|
||||
headers={"Authorization": f"Bearer {tokens.access_token}"},
|
||||
)
|
||||
|
||||
assert account.status_code == 204
|
||||
|
||||
account = await client.get(
|
||||
"https://localhost/api/v1/users/me",
|
||||
headers={"Authorization": f"Bearer {tokens.access_token}"},
|
||||
)
|
||||
|
||||
assert account.status_code == 200
|
||||
assert account.json() == {
|
||||
"created_at": ANY,
|
||||
"disabled": False,
|
||||
"disabled_at": None,
|
||||
"email": "user@localhost.com",
|
||||
"id": ANY,
|
||||
"modified_at": ANY,
|
||||
"name": "awesome",
|
||||
"surname": "bluey",
|
||||
"username": "user",
|
||||
}
|
||||
Reference in New Issue
Block a user