From 86eb1dd4e522af3f2b6cba46e61c854ac64be817 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 14:59:57 +0200 Subject: [PATCH 01/14] class UsersOut --- app/models/users.py | 6 ++++++ app/routers/users.py | 12 ++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/models/users.py b/app/models/users.py index de11fcd..36e15b1 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -8,5 +8,11 @@ class User(BaseModel): roles: str disabled: bool +class UserOut(BaseModel): + id: int + username: str + roles: str + disabled: bool + class UserInDB(User): password: str \ No newline at end of file diff --git a/app/routers/users.py b/app/routers/users.py index 0ec1ccf..33952f7 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -6,10 +6,14 @@ from typing import Annotated router = APIRouter() -@router.get("/users/", tags=["users"], response_model=list[users.User]) -async def read_users(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): - return users_active.fake_users +@router.get("/users/", tags=["users"], response_model=list[users.UserOut]) +async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): + listUsers = [] + for fake in users_active.fake_users: + user = users.UserOut(id=fake["id"], username=fake["username"], disabled=fake["disabled"], roles=fake["roles"]) + listUsers.append(user) + return listUsers -@router.get("/users/me",tags=["users"], response_model=users.User) +@router.get("/users/me",tags=["users"], response_model=users.User, response_model_exclude=["password"]) async def read_users_me(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))]): return current_user \ No newline at end of file -- 2.47.2 From 045f52eeefa7b54ee7a9a6b2e4afe60b2318bc36 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 17:54:46 +0200 Subject: [PATCH 02/14] move variable --- app/dependencies/users_active.py | 2 +- app/routers/token.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/dependencies/users_active.py b/app/dependencies/users_active.py index 9ca2b78..0078b75 100644 --- a/app/dependencies/users_active.py +++ b/app/dependencies/users_active.py @@ -21,7 +21,7 @@ fake_users = [ SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_MINUTES = 30 + pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") diff --git a/app/routers/token.py b/app/routers/token.py index 4d99f28..8467059 100644 --- a/app/routers/token.py +++ b/app/routers/token.py @@ -7,7 +7,7 @@ from ..dependencies import users_active from ..models import token router = APIRouter() - +ACCESS_TOKEN_EXPIRE_MINUTES = 30 @router.post("/token", response_model=token.Token, tags=["token"]) async def login_for_access_token( @@ -20,7 +20,7 @@ async def login_for_access_token( detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) - access_token_expires = timedelta(minutes=users_active.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = users_active.create_access_token( data={"sub": user.username}, expires_delta=access_token_expires ) -- 2.47.2 From b4d44e7bfdc39ea3529ee91524b2591a7b9e4e4f Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 18:15:38 +0200 Subject: [PATCH 03/14] add .env config --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ed8ebf5..6d17870 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -__pycache__ \ No newline at end of file +__pycache__ +.env \ No newline at end of file -- 2.47.2 From 6b0a5a7b65105b9c7f3adf403c0dd7a713399e5e Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 18:17:38 +0200 Subject: [PATCH 04/14] users repository --- app/models/users.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/models/users.py b/app/models/users.py index 36e15b1..743a924 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -1,18 +1,22 @@ from pydantic import BaseModel - +from pydantic_mongo import AbstractRepository, ObjectIdField class User(BaseModel): - id: int + id: ObjectIdField = None username: str password: str roles: str disabled: bool class UserOut(BaseModel): - id: int + id: ObjectIdField = None username: str roles: str disabled: bool class UserInDB(User): - password: str \ No newline at end of file + password: str + +class UserRepository(AbstractRepository[User]): + class Meta: + collection_name = "users" \ No newline at end of file -- 2.47.2 From 680634aeeeefe4be93706c73b3d7d430f549436d Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 21:44:30 +0200 Subject: [PATCH 05/14] add function add startup --- app/dependencies/database.py | 5 +++++ app/dependencies/user_add.py | 11 +++++++++++ app/dependencies/users_active.py | 1 + app/main.py | 7 ++++++- app/routers/users.py | 2 +- 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 app/dependencies/database.py create mode 100644 app/dependencies/user_add.py diff --git a/app/dependencies/database.py b/app/dependencies/database.py new file mode 100644 index 0000000..92c5441 --- /dev/null +++ b/app/dependencies/database.py @@ -0,0 +1,5 @@ +from pymongo import MongoClient +import os + +client = MongoClient(host=os.environ["MONGO_HOST"], username=os.environ["MONGO_USER"], password=os.environ["MONGO_PASSWORD"]) +database = client[os.environ["MONGO_DATABASE"]] \ No newline at end of file diff --git a/app/dependencies/user_add.py b/app/dependencies/user_add.py new file mode 100644 index 0000000..9495e2e --- /dev/null +++ b/app/dependencies/user_add.py @@ -0,0 +1,11 @@ +from ..models import users +from ..dependencies import database +from passlib.context import CryptContext + + +def add(username="", password="", roles="User", disabled=False): + pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + user = users.User(username=username, password=pwd_context.hash(password), roles=roles, disabled=disabled) + user_repository = users.UserRepository(database=database.database) + user_repository.save(user) + print("{O} added".format(username)) diff --git a/app/dependencies/users_active.py b/app/dependencies/users_active.py index 0078b75..a4f04e7 100644 --- a/app/dependencies/users_active.py +++ b/app/dependencies/users_active.py @@ -1,6 +1,7 @@ from datetime import datetime, timedelta from typing import Annotated +from pymongo import MongoClient from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt diff --git a/app/main.py b/app/main.py index 28a1294..6e098ec 100644 --- a/app/main.py +++ b/app/main.py @@ -1,13 +1,18 @@ from fastapi import FastAPI from .routers import users, token - +from .dependencies import user_add app = FastAPI() app.include_router(users.router) app.include_router(token.router) + +@app.on_event("startup") +async def startup_event(): + user_add.add(username="Peter93", password="toto", roles="Admin") + @app.get("/") async def root(): return {"message": "Hello World !"} \ No newline at end of file diff --git a/app/routers/users.py b/app/routers/users.py index 33952f7..52a57e6 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, Depends -from ..dependencies import users_active, permissions_checker +from ..dependencies import users_active, permissions_checker, database from ..models import users from typing import Annotated -- 2.47.2 From 9184d133910cb9c6ed85cd055140213d0ca6d0ff Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 22:18:41 +0200 Subject: [PATCH 06/14] add users work --- app/dependencies/database.py | 2 +- app/dependencies/user_add.py | 15 ++++++++++++--- app/main.py | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/dependencies/database.py b/app/dependencies/database.py index 92c5441..7ff0550 100644 --- a/app/dependencies/database.py +++ b/app/dependencies/database.py @@ -1,5 +1,5 @@ from pymongo import MongoClient import os -client = MongoClient(host=os.environ["MONGO_HOST"], username=os.environ["MONGO_USER"], password=os.environ["MONGO_PASSWORD"]) +client = MongoClient("mongodb+srv://{0}:{1}@{2}/?retryWrites=true&w=majority&appName=AtlasApp".format(os.environ["MONGO_USER"], os.environ["MONGO_PASSWORD"], os.environ["MONGO_HOST"])) database = client[os.environ["MONGO_DATABASE"]] \ No newline at end of file diff --git a/app/dependencies/user_add.py b/app/dependencies/user_add.py index 9495e2e..198c10f 100644 --- a/app/dependencies/user_add.py +++ b/app/dependencies/user_add.py @@ -4,8 +4,17 @@ from passlib.context import CryptContext def add(username="", password="", roles="User", disabled=False): - pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") - user = users.User(username=username, password=pwd_context.hash(password), roles=roles, disabled=disabled) user_repository = users.UserRepository(database=database.database) + + pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + result = user_repository.find_one_by({'username': username}) + change = "added" + user = users.User(username=username, password=pwd_context.hash(password), roles=roles, disabled=disabled) + if result is not None: + result.password=pwd_context.hash(password) + result.roles=roles + result.disabled=disabled + user = result + change = "updated" user_repository.save(user) - print("{O} added".format(username)) + print("{0} {1}".format(username, change)) diff --git a/app/main.py b/app/main.py index 6e098ec..8297dde 100644 --- a/app/main.py +++ b/app/main.py @@ -12,6 +12,7 @@ app.include_router(token.router) @app.on_event("startup") async def startup_event(): user_add.add(username="Peter93", password="toto", roles="Admin") + user_add.add(username="Robert80", password="titi", roles="User") @app.get("/") async def root(): -- 2.47.2 From 02d4257ad6c21d595b465bf18841e992a9de8a71 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 22:35:04 +0200 Subject: [PATCH 07/14] get current user from mongo --- app/dependencies/users_active.py | 29 ++++++++++------------------- app/routers/token.py | 2 +- app/routers/users.py | 2 +- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/app/dependencies/users_active.py b/app/dependencies/users_active.py index a4f04e7..341075e 100644 --- a/app/dependencies/users_active.py +++ b/app/dependencies/users_active.py @@ -8,22 +8,11 @@ from jose import JWTError, jwt from passlib.context import CryptContext from ..models import users, token - - -fake_users = [ - # password foo - {'id': 1, 'username': 'admin', 'password': '$2b$12$N.i74Kle18n5Toxhas.rVOjZreVC2WM34fCidNDyhSNgxVlbKwX7i', - 'roles': 'Admin', 'disabled': False - }, - # password bar - {'id': 2, 'username': 'client', 'password': '$2b$12$KUgpw1m0LF/s9NS1ZB5rRO2cA5D13MqRm56ab7ik2ixftXW/aqEyq', - 'roles':'User', 'disabled':False} -] +from ..dependencies import database SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" ALGORITHM = "HS256" - pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") @@ -34,13 +23,14 @@ def verify_password(plain_password, hashed_password): def get_password_hash(password): return pwd_context.hash(password) -def get_user(db, username: str): - for user in db: - if username == user['username']: - return users.UserInDB(**user) +def get_user(username: str): + user_repository = users.UserRepository(database=database.database) + user = user_repository.find_one_by({'username': username}) + return user -def authenticate_user(fake_db, username: str, password: str): - user = get_user(fake_db, username) +def authenticate_user(username: str, password: str): + + user = get_user(username) if not user: return False if not verify_password(password, user.password): @@ -71,7 +61,8 @@ async def get_current_user(token_str: Annotated[str, Depends(oauth2_scheme)]): token_data = token.TokenData(username=username) except JWTError: raise credentials_exception - user = get_user(fake_users, username=token_data.username) + + user = get_user(token_data.username) if user is None: raise credentials_exception return user diff --git a/app/routers/token.py b/app/routers/token.py index 8467059..55ffdc4 100644 --- a/app/routers/token.py +++ b/app/routers/token.py @@ -13,7 +13,7 @@ ACCESS_TOKEN_EXPIRE_MINUTES = 30 async def login_for_access_token( form_data: Annotated[OAuth2PasswordRequestForm, Depends()] ): - user = users_active.authenticate_user(users_active.fake_users, form_data.username, form_data.password) + user = users_active.authenticate_user(form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, diff --git a/app/routers/users.py b/app/routers/users.py index 52a57e6..dc1498e 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -6,7 +6,7 @@ from typing import Annotated router = APIRouter() -@router.get("/users/", tags=["users"], response_model=list[users.UserOut]) +@router.get("/users", tags=["users"], response_model=list[users.UserOut]) async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): listUsers = [] for fake in users_active.fake_users: -- 2.47.2 From c95e7aa546b3b195458a86e43a8308ddd2ca8171 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 22:55:52 +0200 Subject: [PATCH 08/14] list paginate wip --- app/routers/users.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/routers/users.py b/app/routers/users.py index dc1498e..6d722c5 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -9,6 +9,7 @@ router = APIRouter() @router.get("/users", tags=["users"], response_model=list[users.UserOut]) async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): listUsers = [] + user_repository = users.UserRepository(database=database.database) for fake in users_active.fake_users: user = users.UserOut(id=fake["id"], username=fake["username"], disabled=fake["disabled"], roles=fake["roles"]) listUsers.append(user) -- 2.47.2 From d7e94775722305be6fd18b25fd41edbacf2200de Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 23:32:54 +0200 Subject: [PATCH 09/14] get all users in database --- app/routers/users.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/routers/users.py b/app/routers/users.py index 6d722c5..9f5c13b 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -10,8 +10,9 @@ router = APIRouter() async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): listUsers = [] user_repository = users.UserRepository(database=database.database) - for fake in users_active.fake_users: - user = users.UserOut(id=fake["id"], username=fake["username"], disabled=fake["disabled"], roles=fake["roles"]) + for fake in user_repository.find_by({}): + print(fake) + user = users.UserOut(id=fake.id, username=fake.username, disabled=fake.disabled, roles=fake.roles) listUsers.append(user) return listUsers -- 2.47.2 From 170f66ce2634f8127cefdb134154b79a3e76bffc Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 23:36:51 +0200 Subject: [PATCH 10/14] add limit and skip --- app/routers/users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routers/users.py b/app/routers/users.py index 9f5c13b..6c54c04 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -10,7 +10,7 @@ router = APIRouter() async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): listUsers = [] user_repository = users.UserRepository(database=database.database) - for fake in user_repository.find_by({}): + for fake in user_repository.find_by({}, limit=20, skip=0): print(fake) user = users.UserOut(id=fake.id, username=fake.username, disabled=fake.disabled, roles=fake.roles) listUsers.append(user) -- 2.47.2 From 778e579424725848421eb80379887fbf0db8cdb4 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Fri, 13 Oct 2023 23:40:25 +0200 Subject: [PATCH 11/14] change variable name --- app/routers/users.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/routers/users.py b/app/routers/users.py index 6c54c04..1f7bc49 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -10,9 +10,8 @@ router = APIRouter() async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): listUsers = [] user_repository = users.UserRepository(database=database.database) - for fake in user_repository.find_by({}, limit=20, skip=0): - print(fake) - user = users.UserOut(id=fake.id, username=fake.username, disabled=fake.disabled, roles=fake.roles) + for user_index in user_repository.find_by({}, limit=20, skip=0): + user = users.UserOut(id=user_index.id, username=user_index.username, disabled=user_index.disabled, roles=user_index.roles) listUsers.append(user) return listUsers -- 2.47.2 From 2dedf30095f6072c9e5b8aee14d522af228e48bc Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Sat, 14 Oct 2023 11:53:31 +0200 Subject: [PATCH 12/14] add query limit and skip --- app/routers/users.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/routers/users.py b/app/routers/users.py index 1f7bc49..77e93c1 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -7,10 +7,16 @@ from typing import Annotated router = APIRouter() @router.get("/users", tags=["users"], response_model=list[users.UserOut]) -async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): +async def read_users(skip : int = 0, limit : int = 20, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): + if limit < 1 or skip < 0: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="skip should be greater than 0 and limit should be greater than 1" + ) + limit = limit + skip listUsers = [] user_repository = users.UserRepository(database=database.database) - for user_index in user_repository.find_by({}, limit=20, skip=0): + for user_index in user_repository.find_by({}, limit=limit, skip=skip): user = users.UserOut(id=user_index.id, username=user_index.username, disabled=user_index.disabled, roles=user_index.roles) listUsers.append(user) return listUsers -- 2.47.2 From 340b3e4b524305da06245bc05a919b5028936bb8 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Sat, 14 Oct 2023 15:16:38 +0200 Subject: [PATCH 13/14] fix parameter path --- app/routers/users.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/routers/users.py b/app/routers/users.py index 77e93c1..48c3d83 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException, status from ..dependencies import users_active, permissions_checker, database from ..models import users from typing import Annotated @@ -7,11 +7,11 @@ from typing import Annotated router = APIRouter() @router.get("/users", tags=["users"], response_model=list[users.UserOut]) -async def read_users(skip : int = 0, limit : int = 20, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): - if limit < 1 or skip < 0: +async def read_users(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))], skip: int = 0, limit: int = 20): + if limit < 1 or skip < 0 or limit < skip: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, - detail="skip should be greater than 0 and limit should be greater than 1" + detail="skip should be greater than 0 and limit should be greater than 1. Limit should be greater than skip" ) limit = limit + skip listUsers = [] -- 2.47.2 From 52a196c12d8e587f7f9a4bd1ef9e867a63f7c158 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Sat, 14 Oct 2023 15:48:45 +0200 Subject: [PATCH 14/14] add search get --- app/routers/users.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/app/routers/users.py b/app/routers/users.py index 48c3d83..f97970c 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, HTTPException, status from ..dependencies import users_active, permissions_checker, database from ..models import users from typing import Annotated - +from bson import ObjectId router = APIRouter() @@ -21,6 +21,33 @@ async def read_users(authorize: Annotated[bool, Depends(permissions_checker.Perm listUsers.append(user) return listUsers +@router.get("/users/search", tags=["users"], response_model=list[users.UserOut]) +async def read_users_id(authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))], skip: int = 0, limit: int = 20, key: str | None = None, value: str | None= None): + if limit < 1 or skip < 0 or limit < skip: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="skip should be greater than 0 and limit should be greater than 1. Limit should be greater than skip" + ) + if key is None or value is None: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Key or/and value parameter is empty" + ) + limit = limit + skip + listUsers = [] + user_repository = users.UserRepository(database=database.database) + for user_index in user_repository.find_by({key: {'$regex': value}}, limit=limit, skip=skip): + user = users.UserOut(id=user_index.id, username=user_index.username, disabled=user_index.disabled, roles=user_index.roles) + listUsers.append(user) + return listUsers + + @router.get("/users/me",tags=["users"], response_model=users.User, response_model_exclude=["password"]) async def read_users_me(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))]): - return current_user \ No newline at end of file + return current_user + +@router.get("/users/{item_id}", tags=["users"], response_model=users.User) +async def read_users_id(item_id : str, authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): + user_repository = users.UserRepository(database=database.database) + user = user_repository.find_one_by_id(ObjectId(item_id)) + return user -- 2.47.2