Download tests/test_auth.py from ItsBounvy/bot_host: direct link, hf CLI and curl.
- Browser
- Download file 6.34 kB
-
https://huggingface.co/spaces/ItsBounvy/bot_host/resolve/main/tests/test_auth.py
- Command line
-
hf download hf://spaces/ItsBounvy/bot_host/tests/test_auth.py
-
curl -L -o test_auth.py https://huggingface.co/spaces/ItsBounvy/bot_host/resolve/main/tests/test_auth.py
6.34 kB
| """Unit tests for hosting_panel.auth.SecretVault. | |
| Covers the two-tier encryption roundtrip and the legacy single-tier | |
| fallback path -- this is what protects every bot secret at rest. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| os.environ.setdefault("PANEL_SECRET_KEY", "x" * 64) | |
| os.environ.setdefault("PANEL_ENCRYPTION_KEY", | |
| "90mbg1KLmFKBpoSs5nstm7Ew-p3Q-J3ya3Zbuhwbtho=") | |
| import pytest | |
| from hosting_panel.auth import VAULT | |
| # ---- basics --------------------------------------------------------------- | |
| def test_vault_is_configured_when_env_present(): | |
| assert VAULT.is_configured is True | |
| def test_generate_user_key_is_urlsafe_base64_44chars(): | |
| """User key is a raw Fernet key, not a Fernet-encrypted token.""" | |
| from cryptography.fernet import Fernet | |
| k = VAULT.generate_user_key() | |
| assert isinstance(k, str) | |
| assert len(k) == 44, f"unexpected length: {len(k)}" | |
| # urlsafe-base64 alphabet: A-Z, a-z, 0-9, _, -, = | |
| import re | |
| assert re.fullmatch(r"[A-Za-z0-9_\-=]+", k), f"unexpected chars: {k!r}" | |
| # And the key actually works as a Fernet key (round-trip). | |
| f = Fernet(k.encode()) | |
| ct = f.encrypt(b"hi") | |
| assert f.decrypt(ct) == b"hi" | |
| # ---- single-tier (legacy) ------------------------------------------------- | |
| def test_single_tier_roundtrip(): | |
| ct = VAULT.encrypt("hello") | |
| assert VAULT.decrypt(ct) == "hello" | |
| def test_single_tier_readable_without_user_key(): | |
| ct = VAULT.encrypt("hello") | |
| # user_key=None falls back to single-tier decrypt. | |
| assert VAULT.decrypt(ct, user_key=None) == "hello" | |
| def test_empty_plaintext_passes_through(): | |
| assert VAULT.encrypt("") == "" | |
| assert VAULT.decrypt("") == "" | |
| def test_empty_ciphertext_returns_empty(): | |
| assert VAULT.decrypt(None) == "" | |
| assert VAULT.decrypt("") == "" | |
| # ---- layered -------------------------------------------------------------- | |
| def test_layered_roundtrip(): | |
| user_key = VAULT.generate_user_key() | |
| ct = VAULT.encrypt("secret-payload", user_key=user_key) | |
| # Two layers -> ciphertext is much longer than plaintext. | |
| assert len(ct) > len("secret-payload") * 2 | |
| assert VAULT.decrypt(ct, user_key=user_key) == "secret-payload" | |
| def test_wrong_user_key_falls_back_to_valueerror(): | |
| """Wrong user key on layered data: both paths fail -> ValueError.""" | |
| user_key = VAULT.generate_user_key() | |
| ct = VAULT.encrypt("secret", user_key=user_key) | |
| other_key = VAULT.generate_user_key() | |
| with pytest.raises(ValueError): | |
| VAULT.decrypt(ct, user_key=other_key) | |
| def test_no_user_key_on_layered_fails(): | |
| """Layered data without the user key is NOT silently readable.""" | |
| user_key = VAULT.generate_user_key() | |
| ct = VAULT.encrypt("secret", user_key=user_key) | |
| # No user_key means only single-tier fallback -> can't decrypt layered. | |
| with pytest.raises(ValueError): | |
| VAULT.decrypt(ct, user_key=None) | |
| def test_unicode_payload_roundtrip(): | |
| user_key = VAULT.generate_user_key() | |
| payload = "Привіт, Слава Україні! 🇺🇦 — emoji & unicodé" | |
| ct = VAULT.encrypt(payload, user_key=user_key) | |
| assert VAULT.decrypt(ct, user_key=user_key) == payload | |
| def test_long_payload_roundtrip(): | |
| user_key = VAULT.generate_user_key() | |
| payload = "x" * 100_000 | |
| ct = VAULT.encrypt(payload, user_key=user_key) | |
| assert VAULT.decrypt(ct, user_key=user_key) == payload | |
| def test_layered_then_legacy_fallback_works_on_single_tier_data(): | |
| """Single-tier ciphertext is readable with ANY user_key (fallback path).""" | |
| user_key = VAULT.generate_user_key() | |
| ct = VAULT.encrypt("plain-secret") # single-tier | |
| assert VAULT.decrypt(ct, user_key=user_key) == "plain-secret" | |
| # ---- password hashing ----------------------------------------------------- | |
| def test_password_hash_and_verify(): | |
| from hosting_panel.auth import hash_password, verify_password | |
| h = hash_password("correct-horse-battery-staple") | |
| assert h != "correct-horse-battery-staple" | |
| assert verify_password("correct-horse-battery-staple", h) is True | |
| assert verify_password("wrong-password", h) is False | |
| # Same hash verifies twice (no state mutation). | |
| assert verify_password("correct-horse-battery-staple", h) is True | |
| def test_password_hash_uses_bcrypt_format(): | |
| from hosting_panel.auth import hash_password | |
| h = hash_password("hunter2") | |
| # bcrypt hashes start with $2a$ / $2b$ / $2y$ | |
| assert h.startswith("$2"), f"not a bcrypt hash: {h[:10]}" | |
| # ---- session serializer --------------------------------------------------- | |
| def _make_request(cookies: dict): | |
| """Build a minimal Starlette Request with given cookies.""" | |
| from starlette.requests import Request | |
| scope = { | |
| "type": "http", | |
| "method": "GET", | |
| "path": "/", | |
| "headers": [(b"cookie", f"session={cookies['session']}".encode())] | |
| if "session" in cookies else [], | |
| "query_string": b"", | |
| } | |
| return Request(scope) | |
| def test_session_roundtrip(): | |
| from hosting_panel.auth import create_session, parse_session | |
| token = create_session(user_id=42, username="alice", role="admin") | |
| su = parse_session(token) | |
| assert su is not None | |
| assert su.user_id == 42 | |
| assert su.username == "alice" | |
| assert su.role == "admin" | |
| def test_session_tampering_fails(): | |
| from hosting_panel.auth import create_session, parse_session | |
| token = create_session(user_id=1, username="a", role="admin") | |
| # Flip last two chars. | |
| tampered = token[:-2] + ("AB" if token[-2:] != "AB" else "CD") | |
| assert parse_session(tampered) is None | |
| def test_session_empty_token_returns_none(): | |
| """Garbage / empty input -> None (no crash).""" | |
| from hosting_panel.auth import parse_session | |
| assert parse_session("") is None | |
| assert parse_session("not-a-real-token") is None | |
| assert parse_session("garbage.with.dots") is None | |
| def test_get_session_user_from_cookie(): | |
| """End-to-end: cookie -> SessionUser.""" | |
| from starlette.requests import Request | |
| from hosting_panel.auth import create_session, get_session_user | |
| token = create_session(user_id=7, username="bob", role="operator") | |
| request = _make_request({"session": token}) | |
| su = get_session_user(request) | |
| assert su is not None | |
| assert su.user_id == 7 | |
| assert su.role == "operator" |