diff --git a/src/api/model.py b/src/api/model.py index 6f52d1a..d4d1cb9 100644 --- a/src/api/model.py +++ b/src/api/model.py @@ -57,6 +57,10 @@ def password_check_hash(cls, password: SecretStr) -> str: return hashed_pswd +class ResetPasswordRequest(PasswordHashed): + old_password: SecretStr + + class UserSignupRequest(PasswordHashed): email: EmailStr username: str diff --git a/src/api/profile.py b/src/api/profile.py index c9a76f9..2b21a01 100644 --- a/src/api/profile.py +++ b/src/api/profile.py @@ -1,9 +1,13 @@ from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks from tools.conf import AccountFeaturesConfig, SignupConfig -from api.model import PasswordHashed -import json +from api.model import ResetPasswordRequest +import json, bcrypt from crud.user import change_pswd, update_public_user -from api.dependencies.authenticated import get_pub_user_dep, get_user_dep +from api.dependencies.authenticated import ( + get_pub_user_dep, + get_dangerous_user_dep, + get_user_dep, +) from tools import send_email, all_ids, regenerate_ids, r router = APIRouter( @@ -26,9 +30,9 @@ async def profile(user: dict = Depends(get_pub_user_dep)): @router.post("/reset-password", status_code=204) async def reset_password( - new_password: PasswordHashed, + password_reset_form: ResetPasswordRequest, background_tasks: BackgroundTasks, - user=Depends(get_user_dep), + user=Depends(get_dangerous_user_dep), public_user: dict = Depends(get_pub_user_dep), ): """ @@ -39,6 +43,16 @@ async def reset_password( """ if not AccountFeaturesConfig.enable_reset_pswd: raise HTTPException(status_code=403, detail="Resetting Password is disabled.") + + # If user has old password, validate it, else don't care about that field (maybe he only has OAuth signin so far, so let him reset) + if user.get("password", None): + # Check Password + if not bcrypt.checkpw( + password_reset_form.old_password.get_secret_value().encode("utf-8"), + user["password"].encode("utf-8"), + ): + raise HTTPException(detail="Invalid Old Password", status_code=401) + # Send Confirmation E-Mail (If enabled) if AccountFeaturesConfig.reset_pswd_conf_mail: if r.get("reset_pswd:" + user["email"]): @@ -58,7 +72,7 @@ async def reset_password( { "action": "password_reset", "code": unique_id, - "new_pswd": new_password.password, + "new_pswd": password_reset_form.password, } ), ) @@ -71,7 +85,7 @@ async def reset_password( **public_user, ) else: - change_pswd(user["_id"], new_password.password) + change_pswd(user["_id"], password_reset_form.password) @router.post("/confirm-password", status_code=204) diff --git a/src/crud/user.py b/src/crud/user.py index 0687fa2..7ffcf0b 100644 --- a/src/crud/user.py +++ b/src/crud/user.py @@ -203,19 +203,15 @@ def create_user( str: Session Token """ data = { + **additional_data, **( signup_model.model_dump() if type(signup_model) == UserSignupRequest else {} ), - **additional_data, + "createdAt": datetime.datetime.now(), } # Save the Account into the database try: - user_db = users_collection.insert_one( - { - **data, - "createdAt": datetime.datetime.now(), - } - ) + user_db = users_collection.insert_one(data) except pymongo.errors.DuplicateKeyError: raise HTTPException(detail="Email or Username already exists.", status_code=409) # Drop password from data