mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-09 08:21:50 +00:00
fix(simplex): accept display name in SIMPLEX_ALLOWED_USERS
SIMPLEX_ALLOWED_USERS silently denied every contact when operators listed display names instead of numeric contactIds. The SimpleX UI never surfaces the numeric id, so display names are what operators naturally put in the env var. _is_user_authorized only compared source.user_id (the contactId), so the allowlist never matched. Expand check_ids to include source.user_name for the simplex platform, mirroring the existing WhatsApp phone-LID aliasing pattern. Adds doc + setup-prompt clarification and three regression tests. Salvaged from PR #40393. Adds manishbyatroy to release.py AUTHOR_MAP.
This commit is contained in:
parent
9d72680ca3
commit
490c486ff6
5 changed files with 129 additions and 6 deletions
|
|
@ -7316,6 +7316,21 @@ class GatewayRunner:
|
|||
if normalized_user_id:
|
||||
check_ids.add(normalized_user_id)
|
||||
|
||||
# SimpleX: SIMPLEX_ALLOWED_USERS accepts either the numeric contactId
|
||||
# or the contact's display name. The adapter sets user_id=contactId for
|
||||
# stability across renames, but the SimpleX UI never surfaces the
|
||||
# numeric id — operators only see display names, so that's what they
|
||||
# naturally put in the env var. Match both so the allowlist works
|
||||
# regardless of which form was chosen.
|
||||
# Plugin platform: compare by value since Platform.SIMPLEX is not a
|
||||
# hardcoded enum member (it's a dynamic plugin platform).
|
||||
if (
|
||||
source.platform is not None
|
||||
and source.platform.value == "simplex"
|
||||
and source.user_name
|
||||
):
|
||||
check_ids.add(source.user_name)
|
||||
|
||||
return bool(check_ids & allowed_ids)
|
||||
|
||||
def _get_unauthorized_dm_behavior(self, platform: Optional[Platform]) -> str:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,11 @@ Required environment variables:
|
|||
(default: ws://127.0.0.1:5225)
|
||||
|
||||
Optional environment variables:
|
||||
SIMPLEX_ALLOWED_USERS Comma-separated contact IDs (allowlist)
|
||||
SIMPLEX_ALLOWED_USERS Comma-separated allowlist. Each entry may be
|
||||
either a numeric contactId (stable across
|
||||
renames; visible via `/contacts` in the CLI)
|
||||
or a contact display name (what the SimpleX
|
||||
UI shows). Both forms are accepted.
|
||||
SIMPLEX_ALLOW_ALL_USERS Set 'true' to allow all contacts
|
||||
SIMPLEX_HOME_CHANNEL Default contact/group ID for cron delivery
|
||||
SIMPLEX_HOME_CHANNEL_NAME Human label for the home channel
|
||||
|
|
@ -706,7 +710,7 @@ def interactive_setup() -> None:
|
|||
save_env_value(var, value)
|
||||
|
||||
_prompt("SIMPLEX_WS_URL", "Daemon WebSocket URL (default ws://127.0.0.1:5225)")
|
||||
_prompt("SIMPLEX_ALLOWED_USERS", "Allowed contact IDs (comma-separated; blank=skip)")
|
||||
_prompt("SIMPLEX_ALLOWED_USERS", "Allowed contactIds or display names (comma-separated; blank=skip)")
|
||||
_prompt("SIMPLEX_HOME_CHANNEL", "Home channel contact/group ID (or empty)")
|
||||
print("Done. Make sure the simplex-chat daemon is running before starting the gateway.")
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
|
|||
# Auto-extracted from noreply emails + manual overrides
|
||||
AUTHOR_MAP = {
|
||||
"266365592+bmoore210@users.noreply.github.com": "bmoore210",
|
||||
"manishbyatroy@gmail.com": "manishbyatroy",
|
||||
"chilltulpa@gmail.com": "TheGardenGallery",
|
||||
"al@randomsnowflake.me": "randomsnowflake",
|
||||
"zakame@zakame.net": "zakame",
|
||||
|
|
|
|||
|
|
@ -100,6 +100,109 @@ def test_whatsapp_lid_user_matches_phone_allowlist_via_session_mapping(monkeypat
|
|||
assert runner._is_user_authorized(source) is True
|
||||
|
||||
|
||||
def test_simplex_allowlist_accepts_display_name(monkeypatch):
|
||||
"""SIMPLEX_ALLOWED_USERS should match the contact's display name as well
|
||||
as the numeric contactId. The SimpleX UI surfaces only display names, so
|
||||
operators naturally put those in the env var — and the adapter sets
|
||||
user_id=contactId for stability. Both forms must work. (#TBD)"""
|
||||
_clear_auth_env(monkeypatch)
|
||||
monkeypatch.delenv("SIMPLEX_ALLOWED_USERS", raising=False)
|
||||
monkeypatch.setenv("SIMPLEX_ALLOWED_USERS", "hujikuji")
|
||||
|
||||
# Register the simplex plugin so the env-var lookup resolves.
|
||||
from gateway.platform_registry import platform_registry, PlatformEntry
|
||||
platform_registry.register(PlatformEntry(
|
||||
name="simplex",
|
||||
label="SimpleX Chat",
|
||||
adapter_factory=lambda cfg: None,
|
||||
check_fn=lambda: True,
|
||||
allowed_users_env="SIMPLEX_ALLOWED_USERS",
|
||||
allow_all_env="SIMPLEX_ALLOW_ALL_USERS",
|
||||
))
|
||||
|
||||
simplex = Platform("simplex")
|
||||
runner, _adapter = _make_runner(
|
||||
simplex,
|
||||
GatewayConfig(platforms={simplex: PlatformConfig(enabled=True)}),
|
||||
)
|
||||
|
||||
# contactId in the allowlist would still work — but the operator chose
|
||||
# the display name. Verify the gateway honors it.
|
||||
source = SessionSource(
|
||||
platform=simplex,
|
||||
user_id="4", # adapter sets this to the numeric contactId
|
||||
chat_id="hujikuji",
|
||||
user_name="hujikuji", # adapter sets this to displayName
|
||||
chat_type="dm",
|
||||
)
|
||||
assert runner._is_user_authorized(source) is True
|
||||
|
||||
|
||||
def test_simplex_allowlist_accepts_numeric_contact_id(monkeypatch):
|
||||
"""The numeric contactId form must still work — the new display-name
|
||||
matching must not regress existing setups."""
|
||||
_clear_auth_env(monkeypatch)
|
||||
monkeypatch.delenv("SIMPLEX_ALLOWED_USERS", raising=False)
|
||||
monkeypatch.setenv("SIMPLEX_ALLOWED_USERS", "4")
|
||||
|
||||
from gateway.platform_registry import platform_registry, PlatformEntry
|
||||
platform_registry.register(PlatformEntry(
|
||||
name="simplex",
|
||||
label="SimpleX Chat",
|
||||
adapter_factory=lambda cfg: None,
|
||||
check_fn=lambda: True,
|
||||
allowed_users_env="SIMPLEX_ALLOWED_USERS",
|
||||
allow_all_env="SIMPLEX_ALLOW_ALL_USERS",
|
||||
))
|
||||
|
||||
simplex = Platform("simplex")
|
||||
runner, _adapter = _make_runner(
|
||||
simplex,
|
||||
GatewayConfig(platforms={simplex: PlatformConfig(enabled=True)}),
|
||||
)
|
||||
|
||||
source = SessionSource(
|
||||
platform=simplex,
|
||||
user_id="4",
|
||||
chat_id="hujikuji",
|
||||
user_name="hujikuji",
|
||||
chat_type="dm",
|
||||
)
|
||||
assert runner._is_user_authorized(source) is True
|
||||
|
||||
|
||||
def test_simplex_allowlist_denies_unlisted(monkeypatch):
|
||||
"""Sanity check: an unrelated SimpleX user is still rejected."""
|
||||
_clear_auth_env(monkeypatch)
|
||||
monkeypatch.delenv("SIMPLEX_ALLOWED_USERS", raising=False)
|
||||
monkeypatch.setenv("SIMPLEX_ALLOWED_USERS", "hujikuji")
|
||||
|
||||
from gateway.platform_registry import platform_registry, PlatformEntry
|
||||
platform_registry.register(PlatformEntry(
|
||||
name="simplex",
|
||||
label="SimpleX Chat",
|
||||
adapter_factory=lambda cfg: None,
|
||||
check_fn=lambda: True,
|
||||
allowed_users_env="SIMPLEX_ALLOWED_USERS",
|
||||
allow_all_env="SIMPLEX_ALLOW_ALL_USERS",
|
||||
))
|
||||
|
||||
simplex = Platform("simplex")
|
||||
runner, _adapter = _make_runner(
|
||||
simplex,
|
||||
GatewayConfig(platforms={simplex: PlatformConfig(enabled=True)}),
|
||||
)
|
||||
|
||||
source = SessionSource(
|
||||
platform=simplex,
|
||||
user_id="7",
|
||||
chat_id="stranger",
|
||||
user_name="stranger",
|
||||
chat_type="dm",
|
||||
)
|
||||
assert runner._is_user_authorized(source) is False
|
||||
|
||||
|
||||
def test_star_wildcard_in_allowlist_authorizes_any_user(monkeypatch):
|
||||
"""WHATSAPP_ALLOWED_USERS=* should act as allow-all wildcard."""
|
||||
_clear_auth_env(monkeypatch)
|
||||
|
|
|
|||
|
|
@ -52,20 +52,20 @@ SIMPLEX_HOME_CHANNEL=<contact-id>
|
|||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `SIMPLEX_WS_URL` | Yes | WebSocket URL of the simplex-chat daemon |
|
||||
| `SIMPLEX_ALLOWED_USERS` | Recommended | Comma-separated contact IDs allowed to use the agent |
|
||||
| `SIMPLEX_ALLOWED_USERS` | Recommended | Comma-separated allowlist. Each entry can be a numeric `contactId` **or** a display name — both forms work. |
|
||||
| `SIMPLEX_ALLOW_ALL_USERS` | Optional | Set `true` to allow every contact (use carefully) |
|
||||
| `SIMPLEX_HOME_CHANNEL` | Optional | Default contact ID for cron job delivery |
|
||||
| `SIMPLEX_HOME_CHANNEL_NAME` | Optional | Human label for the home channel |
|
||||
|
||||
## Find your contact ID
|
||||
## Find your contact ID or display name
|
||||
|
||||
After starting the daemon, open a conversation with your agent contact. The contact ID will appear in session logs or via `hermes send_message action=list`.
|
||||
After starting the daemon, open a conversation with your agent contact. The numeric `contactId` appears in session logs or via `hermes send_message action=list`. If you'd rather use the display name shown in the SimpleX UI, that works too — `SIMPLEX_ALLOWED_USERS` accepts either form.
|
||||
|
||||
## Authorization
|
||||
|
||||
By default **all contacts are denied**. You must either:
|
||||
|
||||
1. Set `SIMPLEX_ALLOWED_USERS` to a comma-separated list of contact IDs, or
|
||||
1. Set `SIMPLEX_ALLOWED_USERS` to a comma-separated list of `contactId`s and/or display names (e.g. `SIMPLEX_ALLOWED_USERS=4,alice` matches either contactId 4 or the contact whose display name is "alice"), or
|
||||
2. Use **DM pairing** — send any message to the bot and it will reply with a pairing code. Enter that code via `hermes pairing approve simplex <CODE>`.
|
||||
|
||||
## Using SimpleX with cron jobs
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue