From a7db3862773c90e56db931af6e7fff82abb7a3b6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 5 Sep 2023 17:19:11 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(users.py):=20change=20route?= =?UTF-8?q?=20paths=20for=20user-related=20endpoints=20to=20improve=20cons?= =?UTF-8?q?istency=20and=20readability=20=E2=9C=A8=20feat(users.py):=20add?= =?UTF-8?q?=20support=20for=20resetting=20a=20user's=20password=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(crud.py):=20fix=20update=5Fuser=20function?= =?UTF-8?q?=20to=20properly=20handle=20unchanged=20attributes=20and=20retu?= =?UTF-8?q?rn=20304=20status=20code=20if=20nothing=20is=20updated=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(user.py):=20change=20field=20name=20in=20Use?= =?UTF-8?q?rUpdate=20model=20from=20'username'=20to=20'password'=20to=20re?= =?UTF-8?q?flect=20the=20intended=20functionality=20=F0=9F=90=9B=20fix(ind?= =?UTF-8?q?ex.ts):=20update=20API=20routes=20in=20frontend=20controller=20?= =?UTF-8?q?functions=20to=20match=20the=20changed=20user-related=20endpoin?= =?UTF-8?q?t=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/users.py | 52 +++++++++++++++---- .../services/database/models/user/crud.py | 18 ++++--- .../services/database/models/user/user.py | 2 +- src/frontend/src/controllers/API/index.ts | 10 ++-- 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/backend/langflow/api/v1/users.py b/src/backend/langflow/api/v1/users.py index 517dd7f69..deb350414 100644 --- a/src/backend/langflow/api/v1/users.py +++ b/src/backend/langflow/api/v1/users.py @@ -20,13 +20,14 @@ from langflow.services.auth.utils import ( get_password_hash, ) from langflow.services.database.models.user.crud import ( + get_user_by_id, update_user, ) -router = APIRouter(tags=["Users"]) +router = APIRouter(tags=["Users"], prefix="/users") -@router.post("/user", response_model=UserRead, status_code=201) +@router.post("/", response_model=UserRead, status_code=201) def add_user( user: UserCreate, session: Session = Depends(get_session), @@ -50,7 +51,7 @@ def add_user( return new_user -@router.get("/user", response_model=UserRead) +@router.get("/whoami", response_model=UserRead) def read_current_user( current_user: User = Depends(get_current_active_user), ) -> User: @@ -60,7 +61,7 @@ def read_current_user( return current_user -@router.get("/users", response_model=UsersResponse) +@router.get("/", response_model=UsersResponse) def read_all_users( skip: int = 0, limit: int = 10, @@ -82,20 +83,53 @@ def read_all_users( ) -@router.patch("/user/{user_id}", response_model=UserRead) +@router.patch("/{user_id}", response_model=UserRead) def patch_user( user_id: UUID, - user: UserUpdate, - _: Session = Depends(get_current_active_user), + user_update: UserUpdate, + user: Session = Depends(get_current_active_user), session: Session = Depends(get_session), ) -> User: """ Update an existing user's data. """ - return update_user(user_id, user, session) + if not user.is_superuser and user.id != user_id: + raise HTTPException( + status_code=403, detail="You don't have the permission to update this user" + ) + + if user_db := get_user_by_id(session, user_id): + return update_user(user_db, user_update, session) + else: + raise HTTPException(status_code=404, detail="User not found") -@router.delete("/user/{user_id}") +@router.patch("/{user_id}/reset-password", response_model=UserRead) +def reset_password( + user_id: UUID, + user_update: UserUpdate, + user: Session = Depends(get_current_active_user), + session: Session = Depends(get_session), +) -> User: + """ + Reset a user's password. + """ + if user_id != user.id: + raise HTTPException( + status_code=400, detail="You can't change another user's password" + ) + + if not user: + raise HTTPException(status_code=404, detail="User not found") + + user.password = get_password_hash(user_update.password) + session.commit() + session.refresh(user) + + return user + + +@router.delete("/{user_id}", response_model=dict) def delete_user( user_id: UUID, current_user: User = Depends(get_current_active_superuser), diff --git a/src/backend/langflow/services/database/models/user/crud.py b/src/backend/langflow/services/database/models/user/crud.py index 3dc02a499..93d5dd801 100644 --- a/src/backend/langflow/services/database/models/user/crud.py +++ b/src/backend/langflow/services/database/models/user/crud.py @@ -20,20 +20,24 @@ def get_user_by_id(db: Session, id: UUID) -> Union[User, None]: def update_user( - user_id: UUID, user: UserUpdate, db: Session = Depends(get_session) + user_db: User, user: UserUpdate, db: Session = Depends(get_session) ) -> User: - user_db = get_user_by_id(db, user_id) if not user_db: raise HTTPException(status_code=404, detail="User not found") - user_db_by_username = get_user_by_username(db, user.username) # type: ignore - if user_db_by_username and user_db_by_username.id != user_id: - raise HTTPException(status_code=409, detail="Username already exists") + # user_db_by_username = get_user_by_username(db, user.username) # type: ignore + # if user_db_by_username and user_db_by_username.id != user_id: + # raise HTTPException(status_code=409, detail="Username already exists") user_data = user.dict(exclude_unset=True) + changed = False for attr, value in user_data.items(): if hasattr(user_db, attr) and value is not None: setattr(user_db, attr, value) + changed = True + + if not changed: + raise HTTPException(status_code=304, detail="Nothing to update") user_db.updated_at = datetime.now(timezone.utc) flag_modified(user_db, "updated_at") @@ -49,5 +53,5 @@ def update_user( def update_user_last_login_at(user_id: UUID, db: Session = Depends(get_session)): user_data = UserUpdate(last_login_at=datetime.now(timezone.utc)) # type: ignore - - return update_user(user_id, user_data, db) + user = get_user_by_id(db, user_id) + return update_user(user, user_data, db) diff --git a/src/backend/langflow/services/database/models/user/user.py b/src/backend/langflow/services/database/models/user/user.py index 5f83b4d88..b58f7226a 100644 --- a/src/backend/langflow/services/database/models/user/user.py +++ b/src/backend/langflow/services/database/models/user/user.py @@ -40,7 +40,7 @@ class UserRead(SQLModel): class UserUpdate(SQLModel): - username: Optional[str] = Field() + password: Optional[str] = Field() is_active: Optional[bool] = Field() is_superuser: Optional[bool] = Field() last_login_at: Optional[datetime] = Field() diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 2d18b7cfe..182f2c802 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -402,7 +402,7 @@ export async function renewAccessToken(token: string) { export async function getLoggedUser(): Promise { try { - const res = await api.get(`${BASE_URL_API}user`); + const res = await api.get(`${BASE_URL_API}users/whoami`); if (res.status === 200) { return res.data; @@ -416,7 +416,7 @@ export async function getLoggedUser(): Promise { export async function addUser(user: UserInputType): Promise> { try { - const res = await api.post(`${BASE_URL_API}user`, user); + const res = await api.post(`${BASE_URL_API}users/`, user); if (res.status === 200) { return res.data; } @@ -433,7 +433,7 @@ export async function getUsersPage( ): Promise> { try { const res = await api.get( - `${BASE_URL_API}users?skip=${skip}&limit=${limit}` + `${BASE_URL_API}users/?skip=${skip}&limit=${limit}` ); if (res.status === 200) { return res.data; @@ -447,7 +447,7 @@ export async function getUsersPage( export async function deleteUser(user_id: string) { try { - const res = await api.delete(`${BASE_URL_API}user/${user_id}`); + const res = await api.delete(`${BASE_URL_API}users/${user_id}`); if (res.status === 200) { return res.data; } @@ -459,7 +459,7 @@ export async function deleteUser(user_id: string) { export async function updateUser(user_id: string, user: Users) { try { - const res = await api.patch(`${BASE_URL_API}user/${user_id}`, user); + const res = await api.patch(`${BASE_URL_API}users/${user_id}`, user); if (res.status === 200) { return res.data; }