Add ProxyAwareTransport to fix xmlrpc proxy bypass in Cowork sandbox
Routes all XML-RPC calls through the system HTTPS proxy (HTTPS_PROXY env var) using urllib.request.build_opener(ProxyHandler()) instead of raw socket connections that bypass the proxy. Also adds urllib.request and urllib.error imports.
This commit is contained in:
+27
-2
@@ -9,6 +9,8 @@ Purchase, Inventory, Employees, and Knowledge Templates.
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import xmlrpc.client
|
import xmlrpc.client
|
||||||
|
import urllib.request
|
||||||
|
import urllib.error
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
@@ -18,6 +20,28 @@ ODOO_DB = os.environ.get("ODOO_DB", "mpmedia-odoo-sh-main-13285275")
|
|||||||
ODOO_USERNAME = os.environ.get("ODOO_USERNAME", "bgilliom@mpmedia.tv")
|
ODOO_USERNAME = os.environ.get("ODOO_USERNAME", "bgilliom@mpmedia.tv")
|
||||||
ODOO_API_KEY = os.environ.get("ODOO_API_KEY", "")
|
ODOO_API_KEY = os.environ.get("ODOO_API_KEY", "")
|
||||||
|
|
||||||
|
# ── Proxy-aware XML-RPC transport ─────────────────────────────────────────────
|
||||||
|
class ProxyAwareTransport(xmlrpc.client.SafeTransport):
|
||||||
|
"""Routes xmlrpc through the system HTTPS proxy (respects HTTPS_PROXY env var)."""
|
||||||
|
def request(self, host, handler, request_body, verbose=False):
|
||||||
|
url = f"https://{host}{handler}"
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "text/xml",
|
||||||
|
"Accept-Encoding": "identity",
|
||||||
|
"User-Agent": "xmlrpc-odoo-mpm/1.0",
|
||||||
|
}
|
||||||
|
req = urllib.request.Request(url, request_body, headers)
|
||||||
|
opener = urllib.request.build_opener(urllib.request.ProxyHandler())
|
||||||
|
try:
|
||||||
|
with opener.open(req, timeout=30) as resp:
|
||||||
|
return self.parse_response(resp)
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
raise xmlrpc.client.ProtocolError(url, e.code, e.msg, dict(e.headers))
|
||||||
|
except urllib.error.URLError as e:
|
||||||
|
raise xmlrpc.client.ProtocolError(url, 0, str(e.reason), {})
|
||||||
|
|
||||||
|
_proxy_transport = ProxyAwareTransport()
|
||||||
|
|
||||||
# ── Odoo client ───────────────────────────────────────────────────────────────
|
# ── Odoo client ───────────────────────────────────────────────────────────────
|
||||||
_uid: Optional[int] = None
|
_uid: Optional[int] = None
|
||||||
_models = None
|
_models = None
|
||||||
@@ -26,11 +50,11 @@ def _connect():
|
|||||||
global _uid, _models
|
global _uid, _models
|
||||||
if _uid is not None:
|
if _uid is not None:
|
||||||
return
|
return
|
||||||
common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common")
|
common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common", transport=_proxy_transport)
|
||||||
_uid = common.authenticate(ODOO_DB, ODOO_USERNAME, ODOO_API_KEY, {})
|
_uid = common.authenticate(ODOO_DB, ODOO_USERNAME, ODOO_API_KEY, {})
|
||||||
if not _uid:
|
if not _uid:
|
||||||
raise RuntimeError("Odoo authentication failed. Check ODOO_USERNAME and ODOO_API_KEY.")
|
raise RuntimeError("Odoo authentication failed. Check ODOO_USERNAME and ODOO_API_KEY.")
|
||||||
_models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object")
|
_models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object", transport=_proxy_transport)
|
||||||
|
|
||||||
def _call(model: str, method: str, args=None, kwargs=None):
|
def _call(model: str, method: str, args=None, kwargs=None):
|
||||||
_connect()
|
_connect()
|
||||||
@@ -98,6 +122,7 @@ def get_product(product_id: int) -> dict:
|
|||||||
def get_product_stock(product_id: int) -> list:
|
def get_product_stock(product_id: int) -> list:
|
||||||
"""Get current stock quantities for a product (by product.template ID)
|
"""Get current stock quantities for a product (by product.template ID)
|
||||||
across all internal locations."""
|
across all internal locations."""
|
||||||
|
# Get all product.product IDs under this template
|
||||||
variants = _search_read("product.product",
|
variants = _search_read("product.product",
|
||||||
[["product_tmpl_id", "=", product_id]],
|
[["product_tmpl_id", "=", product_id]],
|
||||||
["id", "display_name"])
|
["id", "display_name"])
|
||||||
|
|||||||
Reference in New Issue
Block a user