users.py 15 KB

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