users.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. import logging
  2. from typing import Optional
  3. import base64
  4. import io
  5. from fastapi import APIRouter, Depends, HTTPException, Request, status
  6. from fastapi.responses import Response, StreamingResponse, FileResponse
  7. from pydantic import BaseModel
  8. from open_webui.models.auths import Auths
  9. from open_webui.models.groups import Groups
  10. from open_webui.models.chats import Chats
  11. from open_webui.models.users import (
  12. UserModel,
  13. UserListResponse,
  14. UserInfoListResponse,
  15. UserRoleUpdateForm,
  16. Users,
  17. UserSettings,
  18. UserUpdateForm,
  19. )
  20. from open_webui.socket.main import (
  21. get_active_status_by_user_id,
  22. get_active_user_ids,
  23. get_user_active_status,
  24. )
  25. from open_webui.constants import ERROR_MESSAGES
  26. from open_webui.env import SRC_LOG_LEVELS, STATIC_DIR
  27. from open_webui.utils.auth import get_admin_user, get_password_hash, get_verified_user
  28. from open_webui.utils.access_control import get_permissions, has_permission
  29. log = logging.getLogger(__name__)
  30. log.setLevel(SRC_LOG_LEVELS["MODELS"])
  31. router = APIRouter()
  32. ############################
  33. # GetActiveUsers
  34. ############################
  35. @router.get("/active")
  36. async def get_active_users(
  37. user=Depends(get_verified_user),
  38. ):
  39. """
  40. Get a list of active users.
  41. """
  42. return {
  43. "user_ids": get_active_user_ids(),
  44. }
  45. ############################
  46. # GetUsers
  47. ############################
  48. PAGE_ITEM_COUNT = 30
  49. @router.get("/", response_model=UserListResponse)
  50. async def get_users(
  51. query: Optional[str] = None,
  52. order_by: Optional[str] = None,
  53. direction: Optional[str] = None,
  54. page: Optional[int] = 1,
  55. user=Depends(get_admin_user),
  56. ):
  57. limit = PAGE_ITEM_COUNT
  58. page = max(1, page)
  59. skip = (page - 1) * limit
  60. filter = {}
  61. if query:
  62. filter["query"] = query
  63. if order_by:
  64. filter["order_by"] = order_by
  65. if direction:
  66. filter["direction"] = direction
  67. return Users.get_users(filter=filter, skip=skip, limit=limit)
  68. @router.get("/all", response_model=UserInfoListResponse)
  69. async def get_all_users(
  70. user=Depends(get_admin_user),
  71. ):
  72. return Users.get_users()
  73. ############################
  74. # User Groups
  75. ############################
  76. @router.get("/groups")
  77. async def get_user_groups(user=Depends(get_verified_user)):
  78. return Groups.get_groups_by_member_id(user.id)
  79. ############################
  80. # User Permissions
  81. ############################
  82. @router.get("/permissions")
  83. async def get_user_permissisions(request: Request, user=Depends(get_verified_user)):
  84. user_permissions = get_permissions(
  85. user.id, request.app.state.config.USER_PERMISSIONS
  86. )
  87. return user_permissions
  88. ############################
  89. # User Default Permissions
  90. ############################
  91. class WorkspacePermissions(BaseModel):
  92. models: bool = False
  93. knowledge: bool = False
  94. prompts: bool = False
  95. tools: bool = False
  96. class SharingPermissions(BaseModel):
  97. public_models: bool = True
  98. public_knowledge: bool = True
  99. public_prompts: bool = True
  100. public_tools: bool = True
  101. class ChatPermissions(BaseModel):
  102. controls: bool = True
  103. valves: bool = True
  104. system_prompt: bool = True
  105. params: bool = True
  106. file_upload: bool = True
  107. delete: bool = True
  108. delete_message: bool = True
  109. continue_response: bool = True
  110. regenerate_response: bool = True
  111. rate_response: bool = True
  112. edit: bool = True
  113. share: bool = True
  114. export: bool = True
  115. stt: bool = True
  116. tts: bool = True
  117. call: bool = True
  118. multiple_models: bool = True
  119. temporary: bool = True
  120. temporary_enforced: bool = False
  121. class FeaturesPermissions(BaseModel):
  122. direct_tool_servers: bool = False
  123. web_search: bool = True
  124. image_generation: bool = True
  125. code_interpreter: bool = True
  126. notes: bool = True
  127. class UserPermissions(BaseModel):
  128. workspace: WorkspacePermissions
  129. sharing: SharingPermissions
  130. chat: ChatPermissions
  131. features: FeaturesPermissions
  132. @router.get("/default/permissions", response_model=UserPermissions)
  133. async def get_default_user_permissions(request: Request, user=Depends(get_admin_user)):
  134. return {
  135. "workspace": WorkspacePermissions(
  136. **request.app.state.config.USER_PERMISSIONS.get("workspace", {})
  137. ),
  138. "sharing": SharingPermissions(
  139. **request.app.state.config.USER_PERMISSIONS.get("sharing", {})
  140. ),
  141. "chat": ChatPermissions(
  142. **request.app.state.config.USER_PERMISSIONS.get("chat", {})
  143. ),
  144. "features": FeaturesPermissions(
  145. **request.app.state.config.USER_PERMISSIONS.get("features", {})
  146. ),
  147. }
  148. @router.post("/default/permissions")
  149. async def update_default_user_permissions(
  150. request: Request, form_data: UserPermissions, user=Depends(get_admin_user)
  151. ):
  152. request.app.state.config.USER_PERMISSIONS = form_data.model_dump()
  153. return request.app.state.config.USER_PERMISSIONS
  154. ############################
  155. # GetUserSettingsBySessionUser
  156. ############################
  157. @router.get("/user/settings", response_model=Optional[UserSettings])
  158. async def get_user_settings_by_session_user(user=Depends(get_verified_user)):
  159. user = Users.get_user_by_id(user.id)
  160. if user:
  161. return user.settings
  162. else:
  163. raise HTTPException(
  164. status_code=status.HTTP_400_BAD_REQUEST,
  165. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  166. )
  167. ############################
  168. # UpdateUserSettingsBySessionUser
  169. ############################
  170. @router.post("/user/settings/update", response_model=UserSettings)
  171. async def update_user_settings_by_session_user(
  172. request: Request, form_data: UserSettings, user=Depends(get_verified_user)
  173. ):
  174. updated_user_settings = form_data.model_dump()
  175. if (
  176. user.role != "admin"
  177. and "toolServers" in updated_user_settings.get("ui").keys()
  178. and not has_permission(
  179. user.id,
  180. "features.direct_tool_servers",
  181. request.app.state.config.USER_PERMISSIONS,
  182. )
  183. ):
  184. # If the user is not an admin and does not have permission to use tool servers, remove the key
  185. updated_user_settings["ui"].pop("toolServers", None)
  186. user = Users.update_user_settings_by_id(user.id, updated_user_settings)
  187. if user:
  188. return user.settings
  189. else:
  190. raise HTTPException(
  191. status_code=status.HTTP_400_BAD_REQUEST,
  192. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  193. )
  194. ############################
  195. # GetUserInfoBySessionUser
  196. ############################
  197. @router.get("/user/info", response_model=Optional[dict])
  198. async def get_user_info_by_session_user(user=Depends(get_verified_user)):
  199. user = Users.get_user_by_id(user.id)
  200. if user:
  201. return user.info
  202. else:
  203. raise HTTPException(
  204. status_code=status.HTTP_400_BAD_REQUEST,
  205. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  206. )
  207. ############################
  208. # UpdateUserInfoBySessionUser
  209. ############################
  210. @router.post("/user/info/update", response_model=Optional[dict])
  211. async def update_user_info_by_session_user(
  212. form_data: dict, user=Depends(get_verified_user)
  213. ):
  214. user = Users.get_user_by_id(user.id)
  215. if user:
  216. if user.info is None:
  217. user.info = {}
  218. user = Users.update_user_by_id(user.id, {"info": {**user.info, **form_data}})
  219. if user:
  220. return user.info
  221. else:
  222. raise HTTPException(
  223. status_code=status.HTTP_400_BAD_REQUEST,
  224. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  225. )
  226. else:
  227. raise HTTPException(
  228. status_code=status.HTTP_400_BAD_REQUEST,
  229. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  230. )
  231. ############################
  232. # GetUserById
  233. ############################
  234. class UserResponse(BaseModel):
  235. name: str
  236. profile_image_url: str
  237. active: Optional[bool] = None
  238. @router.get("/{user_id}", response_model=UserResponse)
  239. async def get_user_by_id(user_id: str, user=Depends(get_verified_user)):
  240. # Check if user_id is a shared chat
  241. # If it is, get the user_id from the chat
  242. if user_id.startswith("shared-"):
  243. chat_id = user_id.replace("shared-", "")
  244. chat = Chats.get_chat_by_id(chat_id)
  245. if chat:
  246. user_id = chat.user_id
  247. else:
  248. raise HTTPException(
  249. status_code=status.HTTP_400_BAD_REQUEST,
  250. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  251. )
  252. user = Users.get_user_by_id(user_id)
  253. if user:
  254. return UserResponse(
  255. **{
  256. "name": user.name,
  257. "profile_image_url": user.profile_image_url,
  258. "active": get_active_status_by_user_id(user_id),
  259. }
  260. )
  261. else:
  262. raise HTTPException(
  263. status_code=status.HTTP_400_BAD_REQUEST,
  264. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  265. )
  266. ############################
  267. # GetUserProfileImageById
  268. ############################
  269. @router.get("/{user_id}/profile/image")
  270. async def get_user_profile_image_by_id(user_id: str, user=Depends(get_verified_user)):
  271. user = Users.get_user_by_id(user_id)
  272. if user:
  273. if user.profile_image_url:
  274. # check if it's url or base64
  275. if user.profile_image_url.startswith("http"):
  276. return Response(
  277. status_code=status.HTTP_302_FOUND,
  278. headers={"Location": user.profile_image_url},
  279. )
  280. elif user.profile_image_url.startswith("data:image"):
  281. try:
  282. header, base64_data = user.profile_image_url.split(",", 1)
  283. image_data = base64.b64decode(base64_data)
  284. image_buffer = io.BytesIO(image_data)
  285. return StreamingResponse(
  286. image_buffer,
  287. media_type="image/png",
  288. headers={"Content-Disposition": "inline; filename=image.png"},
  289. )
  290. except Exception as e:
  291. pass
  292. return FileResponse(f"{STATIC_DIR}/user.png")
  293. else:
  294. raise HTTPException(
  295. status_code=status.HTTP_400_BAD_REQUEST,
  296. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  297. )
  298. ############################
  299. # GetUserActiveStatusById
  300. ############################
  301. @router.get("/{user_id}/active", response_model=dict)
  302. async def get_user_active_status_by_id(user_id: str, user=Depends(get_verified_user)):
  303. return {
  304. "active": get_user_active_status(user_id),
  305. }
  306. ############################
  307. # UpdateUserById
  308. ############################
  309. @router.post("/{user_id}/update", response_model=Optional[UserModel])
  310. async def update_user_by_id(
  311. user_id: str,
  312. form_data: UserUpdateForm,
  313. session_user=Depends(get_admin_user),
  314. ):
  315. # Prevent modification of the primary admin user by other admins
  316. try:
  317. first_user = Users.get_first_user()
  318. if first_user:
  319. if user_id == first_user.id:
  320. if session_user.id != user_id:
  321. # If the user trying to update is the primary admin, and they are not the primary admin themselves
  322. raise HTTPException(
  323. status_code=status.HTTP_403_FORBIDDEN,
  324. detail=ERROR_MESSAGES.ACTION_PROHIBITED,
  325. )
  326. if form_data.role != "admin":
  327. # If the primary admin is trying to change their own role, prevent it
  328. raise HTTPException(
  329. status_code=status.HTTP_403_FORBIDDEN,
  330. detail=ERROR_MESSAGES.ACTION_PROHIBITED,
  331. )
  332. except Exception as e:
  333. log.error(f"Error checking primary admin status: {e}")
  334. raise HTTPException(
  335. status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
  336. detail="Could not verify primary admin status.",
  337. )
  338. user = Users.get_user_by_id(user_id)
  339. if user:
  340. if form_data.email.lower() != user.email:
  341. email_user = Users.get_user_by_email(form_data.email.lower())
  342. if email_user:
  343. raise HTTPException(
  344. status_code=status.HTTP_400_BAD_REQUEST,
  345. detail=ERROR_MESSAGES.EMAIL_TAKEN,
  346. )
  347. if form_data.password:
  348. hashed = get_password_hash(form_data.password)
  349. log.debug(f"hashed: {hashed}")
  350. Auths.update_user_password_by_id(user_id, hashed)
  351. Auths.update_email_by_id(user_id, form_data.email.lower())
  352. updated_user = Users.update_user_by_id(
  353. user_id,
  354. {
  355. "role": form_data.role,
  356. "name": form_data.name,
  357. "email": form_data.email.lower(),
  358. "profile_image_url": form_data.profile_image_url,
  359. },
  360. )
  361. if updated_user:
  362. return updated_user
  363. raise HTTPException(
  364. status_code=status.HTTP_400_BAD_REQUEST,
  365. detail=ERROR_MESSAGES.DEFAULT(),
  366. )
  367. raise HTTPException(
  368. status_code=status.HTTP_400_BAD_REQUEST,
  369. detail=ERROR_MESSAGES.USER_NOT_FOUND,
  370. )
  371. ############################
  372. # DeleteUserById
  373. ############################
  374. @router.delete("/{user_id}", response_model=bool)
  375. async def delete_user_by_id(user_id: str, user=Depends(get_admin_user)):
  376. # Prevent deletion of the primary admin user
  377. try:
  378. first_user = Users.get_first_user()
  379. if first_user and user_id == first_user.id:
  380. raise HTTPException(
  381. status_code=status.HTTP_403_FORBIDDEN,
  382. detail=ERROR_MESSAGES.ACTION_PROHIBITED,
  383. )
  384. except Exception as e:
  385. log.error(f"Error checking primary admin status: {e}")
  386. raise HTTPException(
  387. status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
  388. detail="Could not verify primary admin status.",
  389. )
  390. if user.id != user_id:
  391. result = Auths.delete_auth_by_id(user_id)
  392. if result:
  393. return True
  394. raise HTTPException(
  395. status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
  396. detail=ERROR_MESSAGES.DELETE_USER_ERROR,
  397. )
  398. # Prevent self-deletion
  399. raise HTTPException(
  400. status_code=status.HTTP_403_FORBIDDEN,
  401. detail=ERROR_MESSAGES.ACTION_PROHIBITED,
  402. )
  403. ############################
  404. # GetUserGroupsById
  405. ############################
  406. @router.get("/{user_id}/groups")
  407. async def get_user_groups_by_id(user_id: str, user=Depends(get_admin_user)):
  408. return Groups.get_groups_by_member_id(user_id)