diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/config.py')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/config.py | 58 |
1 files changed, 17 insertions, 41 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/config.py b/packages/meshbay-hub/src/meshbay_hub/config.py index e618510..f710ead 100644 --- a/packages/meshbay-hub/src/meshbay_hub/config.py +++ b/packages/meshbay-hub/src/meshbay_hub/config.py @@ -66,33 +66,15 @@ class JWTConfig: @dataclass class CaptchaConfig: - site_key: str = "" - secret_key: str = "" - # Hostnames a solved captcha may have come from, checked against the one - # `siteverify` reports. Empty means "do not check", which is right while - # the reCAPTCHA key does its own origin check — it is then already done. - # - # Set this when that check is turned off in the reCAPTCHA console, which is - # what the desktop client needs: its page is served from `app://meshbay`, - # so the hostname Google sees is not the hub's and never can be. See - # docs/MESHBAY_DESIGN.md §7.7. - allowed_hosts: list[str] = field(default_factory=list) - # A solve from a page Google cannot attribute to a domain reports an empty - # hostname — `app://meshbay` does, measured live, and so does any other - # non-web client. No `allowed_hosts` entry can match that, and a blank - # entry is not the answer: the parser drops blanks because in a TOML list - # a blank is a typo far more often than an intention. Hence a named flag, - # which also states the trade at the place it is made. - allow_unattributed_host: bool = False - - @property - def enabled(self) -> bool: - return bool(self.site_key and self.secret_key) - - @property - def host_check(self) -> frozenset[str] | None: - """The set to hand `verify_captcha`, or None for "do not check".""" - return frozenset(self.allowed_hosts) if self.allowed_hosts else None + # The proof of work on registration and password reset (captcha.py). On + # unless a configuration turns it off, which is for development and for a + # hub nobody can reach. + enabled: bool = True + # The work asked of the page: it tries numbers from 0 until one matches, so + # half of this on average and all of it at worst. Sized for the slowest + # phone that signs up — about three seconds there, solved while the form is + # being filled in — not for the desktop, which takes a fraction of that. + max_number: int = 500_000 @dataclass @@ -183,12 +165,12 @@ def load_config(path: Path | None = None) -> HubConfig: ): setattr(cfg.mail, name, ml.get(name, getattr(cfg.mail, name))) if cap := raw.get("captcha", {}): - cfg.captcha.site_key = cap.get("site_key", cfg.captcha.site_key) - cfg.captcha.secret_key = cap.get("secret_key", cfg.captcha.secret_key) - if hosts := cap.get("allowed_hosts"): - cfg.captcha.allowed_hosts = [str(h).strip() for h in hosts if str(h).strip()] - if "allow_unattributed_host" in cap: - cfg.captcha.allow_unattributed_host = bool(cap["allow_unattributed_host"]) + cfg.captcha.enabled = bool(cap.get("enabled", cfg.captcha.enabled)) + cfg.captcha.max_number = max(1, int(cap.get("max_number", cfg.captcha.max_number))) + if "site_key" in cap or "secret_key" in cap: + log.warning("captcha.site_key and captcha.secret_key are no longer " + "read: registration is gated by a proof of work, with " + "no third party; remove them from the configuration") break # Env var overrides @@ -204,13 +186,7 @@ def load_config(path: Path | None = None) -> HubConfig: cfg.identity.private_key_path = Path(kp).expanduser() if admin_users := os.environ.get("MESHBAY_ADMIN_USERS"): cfg.identity.admin_usernames = [u.strip() for u in admin_users.split(",") if u.strip()] - if captcha_site := os.environ.get("MESHBAY_CAPTCHA_SITE_KEY"): - cfg.captcha.site_key = captcha_site - if captcha_secret := os.environ.get("MESHBAY_CAPTCHA_SECRET_KEY"): - cfg.captcha.secret_key = captcha_secret - if captcha_hosts := os.environ.get("MESHBAY_CAPTCHA_ALLOWED_HOSTS"): - cfg.captcha.allowed_hosts = [h.strip() for h in captcha_hosts.split(",") if h.strip()] - if unattributed := os.environ.get("MESHBAY_CAPTCHA_ALLOW_UNATTRIBUTED_HOST"): - cfg.captcha.allow_unattributed_host = unattributed.lower() in ("1", "true", "yes") + if captcha := os.environ.get("MESHBAY_CAPTCHA_ENABLED"): + cfg.captcha.enabled = captcha.lower() in ("1", "true", "yes") return cfg |