diff --git a/backend/open_webui/tools/builtin.py b/backend/open_webui/tools/builtin.py index 08aed33aa0..fa8e02bc91 100644 --- a/backend/open_webui/tools/builtin.py +++ b/backend/open_webui/tools/builtin.py @@ -2857,12 +2857,56 @@ async def upload_file_to_terminal( try: from open_webui.models.files import Files from open_webui.storage.provider import Storage + from open_webui.utils.access_control import has_connection_access from open_webui.utils.access_control.files import has_access_to_file user_id = __user__.get('id') user_role = __user__.get('role', 'user') - # --- 1. Retrieve file and check access --- + # --- 1. Resolve terminal connection and build auth --- + cookies = {} + headers = {'X-User-Id': __user__.get('id', '')} + + connections = __request__.app.state.config.TERMINAL_SERVER_CONNECTIONS or [] + connection = next( + (c for c in connections if c.get('id') == terminal_id), None + ) + + if not connection: + return json.dumps({'error': f"Terminal connection '{terminal_id}' not found"}) + + if not await has_connection_access(UserModel(**__user__), connection): + return json.dumps({'error': 'Access denied to terminal server'}) + + terminal_url = connection.get('url', '').rstrip('/') + + # Route through orchestrator policy endpoint if configured + policy_id = connection.get('policy_id') + if policy_id: + terminal_url = f'{terminal_url}/p/{policy_id}' + + # Build auth headers/cookies matching resolve_terminal_tools pattern + auth_type = connection.get('auth_type', 'bearer') + if auth_type == 'bearer': + key = connection.get('key', '') + if key: + headers['Authorization'] = f'Bearer {key}' + elif auth_type == 'session': + cookies = __request__.cookies + if hasattr(__request__, 'state') and hasattr(__request__.state, 'token'): + headers['Authorization'] = f'Bearer {__request__.state.token.credentials}' + elif auth_type == 'system_oauth': + cookies = __request__.cookies + oauth_token = metadata.get('oauth_token') or {} + if oauth_token.get('access_token'): + headers['Authorization'] = f'Bearer {oauth_token["access_token"]}' + # auth_type == 'none': no Authorization header + + chat_id = metadata.get('chat_id') + if chat_id: + headers['X-Session-Id'] = chat_id + + # --- 2. Retrieve file and check access --- file_record = await Files.get_file_by_id(file_id) if not file_record: return json.dumps({'error': 'File not found'}) @@ -2880,65 +2924,6 @@ async def upload_file_to_terminal( local_path = Storage.get_file(file_record.path) - # --- 2. Resolve terminal connection and build auth --- - from open_webui.utils.access_control import has_connection_access - - terminal_url = None - cookies = {} - headers = {'X-User-Id': __user__.get('id', '')} - - # Try system terminal first (terminal_id is a connection ID) - connections = __request__.app.state.config.TERMINAL_SERVER_CONNECTIONS or [] - connection = next( - (c for c in connections if c.get('id') == terminal_id), None - ) - - if connection: - if not await has_connection_access(UserModel(**__user__), connection): - return json.dumps({'error': 'Access denied to terminal server'}) - - terminal_url = connection.get('url', '').rstrip('/') - - # Route through orchestrator policy endpoint if configured - policy_id = connection.get('policy_id') - if policy_id: - terminal_url = f'{terminal_url}/p/{policy_id}' - - # Build auth headers/cookies matching resolve_terminal_tools pattern - auth_type = connection.get('auth_type', 'bearer') - if auth_type == 'bearer': - key = connection.get('key', '') - if key: - headers['Authorization'] = f'Bearer {key}' - elif auth_type == 'session': - cookies = __request__.cookies - if hasattr(__request__, 'state') and hasattr(__request__.state, 'token'): - headers['Authorization'] = f'Bearer {__request__.state.token.credentials}' - elif auth_type == 'system_oauth': - cookies = __request__.cookies - oauth_token = metadata.get('oauth_token') or {} - if oauth_token.get('access_token'): - headers['Authorization'] = f'Bearer {oauth_token["access_token"]}' - # auth_type == 'none': no Authorization header - else: - # Try direct/user-configured terminal (terminal_id is the URL) - direct_tool_servers = metadata.get('tool_servers') or [] - for ts in direct_tool_servers: - ts_url = (ts.get('url') or '').rstrip('/') - if ts_url and ts_url == terminal_id.rstrip('/'): - terminal_url = ts_url - key = ts.get('key', '') - if key: - headers['Authorization'] = f'Bearer {key}' - break - - if not terminal_url: - return json.dumps({'error': f"Terminal connection '{terminal_id}' could not be resolved"}) - - chat_id = metadata.get('chat_id') - if chat_id: - headers['X-Session-Id'] = chat_id - # --- 3. Upload to terminal server --- import aiohttp @@ -2975,7 +2960,7 @@ async def upload_file_to_terminal( headers=headers, cookies=cookies, ) as response: - if response.status != 200: + if not (200 <= response.status < 300): detail = (await response.text())[:500] return json.dumps( {'error': f'Upload failed (HTTP {response.status}): {detail}'} @@ -2994,3 +2979,4 @@ async def upload_file_to_terminal( except Exception as e: log.exception(f'upload_file_to_terminal error: {e}') return json.dumps({'error': str(e)}) +