diff --git a/backend/apps/web/main.py b/backend/apps/web/main.py index 27b857888..30c45d4a0 100644 --- a/backend/apps/web/main.py +++ b/backend/apps/web/main.py @@ -1,7 +1,7 @@ from fastapi import FastAPI, Request, Depends, HTTPException from fastapi.middleware.cors import CORSMiddleware -from apps.web.routers import auths +from apps.web.routers import auths, users from config import OLLAMA_WEBUI_VERSION, OLLAMA_WEBUI_AUTH app = FastAPI() @@ -18,6 +18,7 @@ app.add_middleware( app.include_router(auths.router, prefix="/auths", tags=["auths"]) +app.include_router(users.router, prefix="/users", tags=["users"]) @app.get("/") diff --git a/backend/apps/web/models/users.py b/backend/apps/web/models/users.py index 83fbbb885..e3c871c75 100644 --- a/backend/apps/web/models/users.py +++ b/backend/apps/web/models/users.py @@ -27,6 +27,11 @@ class UserModel(BaseModel): #################### +class UserRoleUpdateForm(BaseModel): + id: str + role: str + + class UsersTable: def __init__(self, db): self.db = db @@ -71,10 +76,19 @@ class UsersTable: def get_users(self, skip: int = 0, limit: int = 50) -> Optional[UserModel]: return [ UserModel(**user) - for user in list(self.table.find({}, {"_id": False})) - .skip(skip) - .limit(limit) + for user in list( + self.table.find({}, {"_id": False}).skip(skip).limit(limit) + ) ] + def update_user_by_id(self, id: str, updated: dict) -> Optional[UserModel]: + user = self.table.find_one_and_update( + {"id": id}, {"$set": updated}, return_document=ReturnDocument.AFTER + ) + return UserModel(**user) + + def update_user_role_by_id(self, id: str, role: str) -> Optional[UserModel]: + return self.update_user_by_id(id, {"role": role}) + Users = UsersTable(DB) diff --git a/backend/apps/web/routers/auths.py b/backend/apps/web/routers/auths.py index 5d4f6182b..c306f4cd9 100644 --- a/backend/apps/web/routers/auths.py +++ b/backend/apps/web/routers/auths.py @@ -8,15 +8,6 @@ from pydantic import BaseModel import time import uuid -from constants import ERROR_MESSAGES -from utils.utils import ( - get_password_hash, - bearer_scheme, - create_token, -) - -from utils.misc import get_gravatar_url - from apps.web.models.auths import ( SigninForm, SignupForm, @@ -25,13 +16,19 @@ from apps.web.models.auths import ( Auths, ) from apps.web.models.users import Users -import config + + +from utils.utils import ( + get_password_hash, + bearer_scheme, + create_token, +) +from utils.misc import get_gravatar_url +from constants import ERROR_MESSAGES + router = APIRouter() -DB = config.DB - - ############################ # GetSessionUser ############################ diff --git a/backend/apps/web/routers/users.py b/backend/apps/web/routers/users.py new file mode 100644 index 000000000..08437bd34 --- /dev/null +++ b/backend/apps/web/routers/users.py @@ -0,0 +1,75 @@ +from fastapi import Response +from fastapi import Depends, FastAPI, HTTPException, status +from datetime import datetime, timedelta +from typing import List, Union, Optional + +from fastapi import APIRouter +from pydantic import BaseModel +import time +import uuid + +from apps.web.models.users import UserModel, UserRoleUpdateForm, Users + +from utils.utils import ( + get_password_hash, + bearer_scheme, + create_token, +) +from constants import ERROR_MESSAGES + +router = APIRouter() + +############################ +# GetUsers +############################ + + +@router.get("/", response_model=List[UserModel]) +async def get_users(skip: int = 0, limit: int = 50, cred=Depends(bearer_scheme)): + token = cred.credentials + user = Users.get_user_by_token(token) + + if user: + if user.role == "admin": + return Users.get_users(skip, limit) + else: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=ERROR_MESSAGES.INVALID_TOKEN, + ) + + +############################ +# UpdateUserRole +############################ + + +@router.post("/update/role", response_model=Optional[UserModel]) +async def update_user_role(form_data: UserRoleUpdateForm, cred=Depends(bearer_scheme)): + token = cred.credentials + user = Users.get_user_by_token(token) + + if user: + if user.role == "admin": + if user.id != form_data.id: + return Users.update_user_role_by_id(form_data.id, form_data.role) + else: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.ACTION_PROHIBITED, + ) + else: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) + else: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=ERROR_MESSAGES.INVALID_TOKEN, + ) diff --git a/backend/constants.py b/backend/constants.py index 8729f7a5b..50c767220 100644 --- a/backend/constants.py +++ b/backend/constants.py @@ -13,6 +13,8 @@ class ERROR_MESSAGES(str, Enum): INVALID_CRED = "The email or password provided is incorrect. Please check for typos and try logging in again." UNAUTHORIZED = "401 Unauthorized" ACCESS_PROHIBITED = "You do not have permission to access this resource. Please contact your administrator for assistance." - NOT_FOUND = "We could not find what you're looking for :/" + ACTION_PROHIBITED = ( + "The requested action has been restricted as a security measure." + ) USER_NOT_FOUND = "We could not find what you're looking for :/" MALICIOUS = "Unusual activities detected, please try again in a few minutes." diff --git a/src/lib/components/chat/SettingsModal.svelte b/src/lib/components/chat/SettingsModal.svelte index 07fe96ea2..3fc016d57 100644 --- a/src/lib/components/chat/SettingsModal.svelte +++ b/src/lib/components/chat/SettingsModal.svelte @@ -389,31 +389,33 @@