Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
"""Gateway authz tests: BUZZ_ALLOWED_USERS accepts npub or hex (#78428).
|
||||
|
||||
Inbound Buzz events carry the sender as a 64-char hex pubkey, while
|
||||
``BUZZ_ALLOWED_USERS`` historically accepted npubs too. The gateway's
|
||||
central allowlist comparison must decode npub entries to hex at comparison
|
||||
time, or an operator who listed only their npub is rejected with
|
||||
"Unauthorized user: <hex pubkey>" (gateway drops the message).
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from gateway.config import Platform
|
||||
from gateway.platform_registry import PlatformEntry, platform_registry
|
||||
from gateway.session import SessionSource
|
||||
|
||||
# Chip's public identity (public information, not a secret) — the same pair
|
||||
# used by tests/gateway/test_buzz_adapter.py.
|
||||
SELF_PUBKEY = "9fd5c7ba6d3ef224da78f541e0fcb9c50f72cc63edb19aae76ac6a0474dfa860"
|
||||
SELF_NPUB = "npub1nl2u0wnd8mezfknc74q7pl9ec58h9nrrakce4tnk434qgaxl4psqe5twr6"
|
||||
OTHER_PUBKEY = "a" * 64
|
||||
|
||||
_BUZZ_PLATFORM = Platform("buzz")
|
||||
|
||||
_AUTH_ENV_VARS = (
|
||||
"BUZZ_ALLOWED_USERS",
|
||||
"BUZZ_ALLOW_ALL_USERS",
|
||||
"GATEWAY_ALLOWED_USERS",
|
||||
"GATEWAY_ALLOW_ALL_USERS",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _isolate_env(monkeypatch):
|
||||
for var in _AUTH_ENV_VARS:
|
||||
monkeypatch.delenv(var, raising=False)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def buzz_registered():
|
||||
"""Register a minimal Buzz platform entry (allowed_users_env contract)."""
|
||||
platform_registry.register(
|
||||
PlatformEntry(
|
||||
name="buzz",
|
||||
label="Buzz",
|
||||
adapter_factory=lambda cfg: None,
|
||||
check_fn=lambda: True,
|
||||
allowed_users_env="BUZZ_ALLOWED_USERS",
|
||||
allow_all_env="BUZZ_ALLOW_ALL_USERS",
|
||||
)
|
||||
)
|
||||
yield
|
||||
platform_registry.unregister("buzz")
|
||||
|
||||
|
||||
def _make_runner():
|
||||
from gateway.run import GatewayRunner
|
||||
|
||||
runner = object.__new__(GatewayRunner)
|
||||
runner.pairing_store = None
|
||||
return runner
|
||||
|
||||
|
||||
def _make_source(user_id: str, chat_type: str = "dm"):
|
||||
return SessionSource(
|
||||
platform=_BUZZ_PLATFORM,
|
||||
chat_id="ccc2bc1a-7a82-5a8f-8c4e-57a070cbe7cd",
|
||||
chat_type=chat_type,
|
||||
user_id=user_id,
|
||||
user_name="Chip",
|
||||
is_bot=False,
|
||||
)
|
||||
|
||||
|
||||
def test_npub_only_allowlist_authorizes_hex_identity(monkeypatch, buzz_registered):
|
||||
"""The reported bug: listing only the npub must authorize the hex pubkey."""
|
||||
monkeypatch.setenv("BUZZ_ALLOWED_USERS", SELF_NPUB)
|
||||
runner = _make_runner()
|
||||
assert runner._is_user_authorized(_make_source(SELF_PUBKEY)) is True
|
||||
|
||||
|
||||
def test_hex_only_allowlist_still_authorizes(monkeypatch, buzz_registered):
|
||||
"""Existing hex-only allowlists keep working unchanged."""
|
||||
monkeypatch.setenv("BUZZ_ALLOWED_USERS", SELF_PUBKEY)
|
||||
runner = _make_runner()
|
||||
assert runner._is_user_authorized(_make_source(SELF_PUBKEY)) is True
|
||||
|
||||
|
||||
def test_mixed_npub_and_hex_allowlist_authorizes(monkeypatch, buzz_registered):
|
||||
"""Both forms in one allowlist authorize the same identity."""
|
||||
monkeypatch.setenv("BUZZ_ALLOWED_USERS", f"{SELF_NPUB},{SELF_PUBKEY}")
|
||||
runner = _make_runner()
|
||||
assert runner._is_user_authorized(_make_source(SELF_PUBKEY)) is True
|
||||
|
||||
|
||||
def test_npub_allowlist_still_denies_other_user(monkeypatch, buzz_registered):
|
||||
"""Normalization must not turn into fail-open for unrelated senders."""
|
||||
monkeypatch.setenv("BUZZ_ALLOWED_USERS", SELF_NPUB)
|
||||
runner = _make_runner()
|
||||
assert runner._is_user_authorized(_make_source(OTHER_PUBKEY)) is False
|
||||
|
||||
|
||||
def test_uppercase_npub_allowlist_authorizes(monkeypatch, buzz_registered):
|
||||
"""npub entries are case-insensitive, like the adapter's own decoder."""
|
||||
monkeypatch.setenv("BUZZ_ALLOWED_USERS", SELF_NPUB.upper())
|
||||
runner = _make_runner()
|
||||
assert runner._is_user_authorized(_make_source(SELF_PUBKEY)) is True
|
||||
|
||||
|
||||
def test_normalize_nostr_allow_entries():
|
||||
from gateway.authz_mixin import _normalize_nostr_allow_entries
|
||||
|
||||
expanded = _normalize_nostr_allow_entries({SELF_NPUB, OTHER_PUBKEY, "not-a-key"})
|
||||
assert SELF_PUBKEY in expanded
|
||||
assert SELF_NPUB in expanded # original kept, harmless
|
||||
assert OTHER_PUBKEY in expanded
|
||||
assert "not-a-key" in expanded
|
||||
|
||||
|
||||
def test_npub_to_hex_roundtrip():
|
||||
from gateway.authz_mixin import _npub_to_hex
|
||||
|
||||
assert _npub_to_hex(SELF_NPUB) == SELF_PUBKEY
|
||||
assert _npub_to_hex(SELF_PUBKEY) is None # hex input is not an npub
|
||||
assert _npub_to_hex("npub1garbage!!") is None # invalid bech32
|
||||
assert _npub_to_hex("") is None
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# #82871: single-profile gateway must consult the Buzz adapter's own
|
||||
# config.extra.allowed_users when no env allowlist is configured. The
|
||||
# reported symptom was a default-deny of EVERY Buzz user ("Unauthorized
|
||||
# user: <hex> on buzz") even though the sender's npub was correctly
|
||||
# listed in gateway.platforms.buzz.extra.allowed_users.
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _make_runner_with_adapter(extra: dict):
|
||||
"""Single-profile (multiplex OFF) runner with a live Buzz adapter."""
|
||||
from types import SimpleNamespace
|
||||
|
||||
from gateway.authz_mixin import _npub_to_hex
|
||||
from gateway.config import GatewayConfig, PlatformConfig
|
||||
from gateway.run import GatewayRunner
|
||||
|
||||
def _normalize(entry):
|
||||
entry = (entry or "").strip()
|
||||
if entry.lower().startswith("npub1"):
|
||||
return _npub_to_hex(entry)
|
||||
return entry.lower() if entry else None
|
||||
|
||||
runner = object.__new__(GatewayRunner)
|
||||
runner.config = GatewayConfig()
|
||||
runner.pairing_store = None
|
||||
runner.adapters = {
|
||||
_BUZZ_PLATFORM: SimpleNamespace(
|
||||
config=PlatformConfig(enabled=True, extra=extra),
|
||||
normalize_user_id=_normalize,
|
||||
)
|
||||
}
|
||||
return runner
|
||||
|
||||
|
||||
def test_config_only_npub_allowlist_authorizes_single_profile(buzz_registered):
|
||||
"""#82871 repro: npub listed ONLY in config.extra.allowed_users (no env
|
||||
var at all) must authorize the sender's hex pubkey."""
|
||||
runner = _make_runner_with_adapter({"allowed_users": [SELF_NPUB]})
|
||||
assert runner._is_user_authorized(_make_source(SELF_PUBKEY)) is True
|
||||
|
||||
|
||||
def test_config_only_hex_allowlist_authorizes_single_profile(buzz_registered):
|
||||
runner = _make_runner_with_adapter({"allowed_users": [SELF_PUBKEY]})
|
||||
assert runner._is_user_authorized(_make_source(SELF_PUBKEY)) is True
|
||||
|
||||
|
||||
def test_config_only_allowlist_still_denies_unlisted_sender(buzz_registered):
|
||||
"""Consulting the adapter allowlist must not fail open."""
|
||||
runner = _make_runner_with_adapter({"allowed_users": [SELF_NPUB]})
|
||||
assert runner._is_user_authorized(_make_source(OTHER_PUBKEY)) is False
|
||||
|
||||
|
||||
def test_config_only_empty_allowlist_keeps_default_deny(buzz_registered):
|
||||
"""No allowlist anywhere: the default-deny is preserved (SECURITY.md §2.6)."""
|
||||
runner = _make_runner_with_adapter({})
|
||||
assert runner._is_user_authorized(_make_source(SELF_PUBKEY)) is False
|
||||
Reference in New Issue
Block a user