diff --git a/core/jira_tool.py b/core/jira_tool.py index ef5db00..285ecfb 100644 --- a/core/jira_tool.py +++ b/core/jira_tool.py @@ -83,18 +83,40 @@ def get_issue_by_url(config: Dict[str, Any] | None, url: str) -> str: return get_issue(config, key) +def _is_cloud(base_url: str) -> bool: + """True when the base URL points at Atlassian Cloud (*.atlassian.net).""" + try: + host = (urlparse(base_url).hostname or "").lower() + except ValueError: + return False + return host.endswith(".atlassian.net") + + def _get(config: Dict[str, Any], path: str, params: dict = None): - """Gọi Jira REST API bằng xác thực cơ bản, qua lớp TLS có ghim chứng chỉ nội bộ.""" + """Gọi Jira REST API, tự chọn mode xác thực theo loại server. + + Jira Cloud (*.atlassian.net) → Basic Auth (email + API token). + Jira Server / Data Center → Bearer token (Personal Access Token). + Cả hai đều đi qua lớp TLS có ghim chứng chỉ nội bộ (tls_trust). + """ from . import tls_trust c = _conf(config) url = c["base_url"].rstrip("/") + path - # Same TLS auto-recovery the LLM provider calls get (core/tls_trust.py) — - # a corporate gateway that terminates TLS with its own certificate used to - # break this outright with SSLCertVerificationError. - resp = tls_trust.request("get", url, params=params or {}, timeout=_TIMEOUT, - auth=(c["email"], c["api_token"]), - headers={"Accept": "application/json"}) + headers = {"Accept": "application/json"} + + if _is_cloud(c["base_url"]): + # Cloud: Basic Auth với email + API token từ id.atlassian.com + resp = tls_trust.request("get", url, params=params or {}, timeout=_TIMEOUT, + auth=(c["email"], c["api_token"]), + headers=headers) + else: + # Server / Data Center: Personal Access Token qua Bearer header. + # Người dùng dán PAT vào trường "API token" trong UI Connectors. + headers["Authorization"] = f"Bearer {c['api_token']}" + resp = tls_trust.request("get", url, params=params or {}, timeout=_TIMEOUT, + headers=headers) + resp.raise_for_status() return resp.json()