"""Beta-gate access policy — who may use the dev Space and see the Review tab. Separate from ``auth.py`` (identity resolution) on purpose: this module owns the *policy* (allowlist / owner / enforcement), reusing ``resolve_user`` for the *identity*. The whole module is inert unless ``BETA_GATE_ENABLED`` is set, so it is a no-op on prod and local. """ from __future__ import annotations from typing import Any import gradio as gr from config import BETA_ALLOWLIST, BETA_GATE_ENABLED, REVIEW_TAB_OWNERS from src.sessions.auth import resolve_user # Shown in the gate overlay to a signed-in user who isn't on the allowlist. # Polite "no access / request access" — acknowledges restricted access. # TODO: replace with the real request-access link/handle at deploy time. BETA_REJECT_MESSAGE = ( "### You don't have access to this space\n" "This is a restricted beta. Your Hugging Face account isn't on the access " "list yet — [request access](mailto:ahmed.ibrahim8165@gmail.com) and we'll " "add you." ) # Raised at compute entry points for a non-allowlisted (incl. anonymous/API) caller. _ENFORCE_MESSAGE = ( "This Space is in restricted beta. Please sign in with an approved " "Hugging Face account to use it." ) def _username(profile: Any | None) -> str | None: """Resolved, normalized (lowercase) HF username, or None if unauthenticated.""" user = resolve_user(profile) return user.username.lower() if user else None def is_review_owner(profile: Any | None) -> bool: """True if the signed-in user is an owner who may see the Review tab.""" name = _username(profile) return bool(name and name in REVIEW_TAB_OWNERS) def is_beta_allowed(profile: Any | None) -> bool: """True if the caller may use the app. When the gate is disabled (prod/local) everyone is allowed. When enabled, anonymous callers are blocked and signed-in callers must be on the allowlist (owners are implicitly allowed so they can never self-lock-out). """ if not BETA_GATE_ENABLED: return True name = _username(profile) if not name: return False return name in BETA_ALLOWLIST or name in REVIEW_TAB_OWNERS def enforce_beta_access(profile: Any | None) -> None: """Raise ``gr.Error`` if the gate is on and the caller isn't allowlisted. Server-side counterpart to the UI overlay: rejects scripted/API callers (``profile`` is None for them) and signed-in-but-not-allowlisted users. """ if not is_beta_allowed(profile): raise gr.Error(_ENFORCE_MESSAGE)