"""App-layer identity & authorization — the family allow-list gate. Deliberately mirrors the sibling specialists' ``access.py`` env contract (pdac-genomics-agent, pdac-subtype-agent ADR-0012, DecoupleRpy_Agent ``access_control``) so all Spaces share ONE allow-list model: - ``ADMIN_IDS`` — comma-separated admin identities (``UPLOAD_ADMIN_IDS`` honored for back-compat with the family contract). - ``ALLOWED_IDS`` — comma-separated regular-user identities. Admins are allowed too. - ``ACCESS_CONTROL`` — dark-launch switch. OFF unless one of ``1/on/true/enforce/yes``. It is set to enforce on both Spaces before either is made public (plan D2-C). When ON, fail-closed: an empty list denies everyone; unknown/anonymous identities are refused. """ from __future__ import annotations import os ADMIN_ROLE = "admin" USER_ROLE = "user" _ADMIN_ENV_VARS = ("ADMIN_IDS", "UPLOAD_ADMIN_IDS") _ALLOWED_ENV_VAR = "ALLOWED_IDS" _ENFORCE_VALUES = {"1", "on", "true", "enforce", "yes"} def _ids_from_env(*env_vars: str) -> set[str]: ids: set[str] = set() for var in env_vars: raw = os.environ.get(var, "") ids.update(part.strip() for part in raw.split(",") if part.strip()) return ids def access_enforced() -> bool: return os.environ.get("ACCESS_CONTROL", "").strip().lower() in _ENFORCE_VALUES def role_for(identity: str | None) -> str | None: if not identity: return None identity = identity.strip() if not identity: return None if identity in _ids_from_env(*_ADMIN_ENV_VARS): return ADMIN_ROLE if identity in _ids_from_env(_ALLOWED_ENV_VAR): return USER_ROLE return None def is_authorized(identity: str | None) -> bool: return role_for(identity) is not None def check_access(username: str | None) -> tuple[bool, str | None]: """(allowed, denial_message). Enforcement off ⇒ everyone allowed.""" if not access_enforced(): return True, None if not username: return False, ( "🔒 Please sign in with your HuggingFace account (button above) to use this tool." ) if not is_authorized(username): return False, ( f"🔒 Access denied — '{username}' is not on the authorized-users list " "for this deployment. Contact the study admin to request access." ) return True, None