mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-06-24 00:39:30 +08:00
Self-contained package under app/model_downloader/: - Allowlist + path-validated downloads (SSRF guard: HF/Civitai/localhost + model extension). - Streaming worker: writes to <final>.tmp, atomic rename on success, cooperative cancellation with epoch-based session identity, orphan .tmp sweep. - Unified availability probe with per-URL gated/size caching; is_hf_downloadable recomputed per call so login/license changes surface within one poll. - HuggingFace OAuth 2.0 PKCE flow with loopback callback server and on-disk (0600) token storage + transparent refresh. - Pydantic request/response schemas and aiohttp routes under api/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
"""Whether this deployment is allowed to do interactive HF OAuth.
|
|
|
|
We only let the server hold a HuggingFace token under a strict trust
|
|
assumption: this is a *single tenant local* install. Concretely:
|
|
|
|
- The server is bound to a loopback address. SSH tunneling /
|
|
reverse-proxies can defeat this, but it's the strongest signal
|
|
we have without an authentication system.
|
|
- ``--multi-user`` is off. A shared token used implicitly by multiple
|
|
declared users would be a footgun — one user's gated downloads
|
|
would silently authenticate as another.
|
|
|
|
Anything else and the frontend hides the HF login UI entirely; gated
|
|
models continue to show the "acquire it manually" message.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ipaddress
|
|
import socket
|
|
|
|
from comfy.cli_args import args
|
|
|
|
|
|
def _is_loopback(host: str | None) -> bool:
|
|
"""Duplicates ``server.is_loopback`` (small, no shared module yet).
|
|
|
|
Resolves a host or IP literal to whether it lives on the loopback
|
|
interface (127.0.0.0/8 for IPv4, ::1 for IPv6). Returns False for
|
|
``0.0.0.0`` / ``::`` because those are bind-all wildcards, not
|
|
loopback.
|
|
"""
|
|
if host is None:
|
|
return False
|
|
try:
|
|
return ipaddress.ip_address(host).is_loopback
|
|
except ValueError:
|
|
pass
|
|
|
|
loopback = False
|
|
for family in (socket.AF_INET, socket.AF_INET6):
|
|
try:
|
|
r = socket.getaddrinfo(host, None, family, socket.SOCK_STREAM)
|
|
for _family, _, _, _, sockaddr in r:
|
|
if not ipaddress.ip_address(sockaddr[0]).is_loopback:
|
|
return loopback
|
|
loopback = True
|
|
except socket.gaierror:
|
|
pass
|
|
return loopback
|
|
|
|
|
|
def is_hf_auth_eligible() -> bool:
|
|
"""True iff this deployment may surface the HF OAuth flow."""
|
|
return _is_loopback(args.listen) and not args.multi_user
|