Replace token auth with single-user login

This commit is contained in:
Dymas
2026-05-25 16:30:01 +02:00
parent 8549944be1
commit 1487257bed
8 changed files with 237 additions and 136 deletions
+59 -7
View File
@@ -2,6 +2,7 @@
"""Shared constants and helper utilities for ani-cli-web."""
import hashlib
import hmac
import ipaddress
import json
@@ -19,7 +20,7 @@ from uuid import uuid4
PROJECT_ROOT = Path(__file__).resolve().parent
ANI_CLI = os.environ.get("ANI_CLI_BIN") or shutil.which("ani-cli") or "ani-cli"
APP_NAME = "ani-cli-web"
VERSION = "0.40.6"
VERSION = "0.41.0"
ALLANIME_BASE = "allanime.day"
ALLANIME_API = f"https://api.{ALLANIME_BASE}"
ALLANIME_REFERER = "https://allmanga.to"
@@ -82,17 +83,56 @@ def remote_access_allowed():
return env_flag("ANI_CLI_WEB_ALLOW_REMOTE")
def remote_access_token():
return str(os.environ.get("ANI_CLI_WEB_REMOTE_TOKEN") or "").strip()
def remote_access_username(config=None):
configured = str(os.environ.get("ANI_CLI_WEB_AUTH_USERNAME") or "").strip()
if configured:
return configured
if isinstance(config, dict):
return str(config.get("auth_username") or "").strip()
return ""
def remote_access_token_configured():
return bool(remote_access_token())
def remote_access_password(config=None):
configured = str(os.environ.get("ANI_CLI_WEB_AUTH_PASSWORD") or "").strip()
if configured:
return configured
if isinstance(config, dict):
return str(config.get("auth_password") or "").strip()
return ""
def remote_access_token_matches(value):
def remote_access_credentials_configured(config=None):
return bool(remote_access_username(config) and remote_access_password(config))
def remote_access_credentials_match(username, password, config=None):
expected_username = remote_access_username(config)
expected_password = remote_access_password(config)
provided_username = str(username or "").strip()
provided_password = str(password or "").strip()
return bool(
expected_username
and expected_password
and hmac.compare_digest(provided_username, expected_username)
and hmac.compare_digest(provided_password, expected_password)
)
def remote_access_session_value(config=None):
username = remote_access_username(config)
password = remote_access_password(config)
if not username or not password:
return ""
return hmac.new(
f"{username}\0{password}".encode("utf-8"),
b"ani-cli-web-session",
hashlib.sha256,
).hexdigest()
def remote_access_session_matches(value, config=None):
provided = str(value or "").strip()
expected = remote_access_token()
expected = remote_access_session_value(config)
return bool(expected and provided and hmac.compare_digest(provided, expected))
@@ -145,7 +185,10 @@ DEFAULT_CONFIG = {
"auto_download_quality": "best",
"discord_webhook_url": "",
"discord_webhook_events": [],
"auth_username": str(os.environ.get("ANI_CLI_WEB_AUTH_USERNAME") or "").strip(),
"auth_password": str(os.environ.get("ANI_CLI_WEB_AUTH_PASSWORD") or "").strip(),
}
PRIVATE_CONFIG_KEYS = {"auth_username", "auth_password"}
def now_iso():
@@ -295,6 +338,8 @@ def normalize_config(data):
auto_download_quality = auto_download_quality[:-1]
webhook_url = str(config.get("discord_webhook_url") or "").strip()
webhook_events = config.get("discord_webhook_events")
auth_username = str(config.get("auth_username") or "").strip()
auth_password = str(config.get("auth_password") or "").strip()
if isinstance(auto_refresh_enabled, str):
auto_refresh_enabled = auto_refresh_enabled.strip().lower() in {"1", "true", "yes", "on"}
@@ -344,9 +389,16 @@ def normalize_config(data):
normalized_events.append(normalized_name)
config["discord_webhook_url"] = webhook_url
config["discord_webhook_events"] = normalized_events
config["auth_username"] = auth_username
config["auth_password"] = auth_password
return config
def sanitize_public_config(config):
source = normalize_config(config or {})
return {key: value for key, value in source.items() if key not in PRIVATE_CONFIG_KEYS}
def discord_webhook_enabled(config, event_name, force=False):
if not isinstance(config, dict):
return False