fix(secrets): route authz gate and pairing allowlist reads through the profile secret scope
- gateway/authz_mixin.py: group chat allowlists, {PLATFORM}_ALLOW_BOTS, and
pairing-mode allowlist presence checks now go through the file's own
_platform_gate_env helper (scoped-authoritative under multiplex).
- gateway/pairing.py: grant-mirror/revoke allowlist READS go through
get_secret (Slack pattern for unscoped admin/CLI callers); writes still
use save_env_value with a TODO for profile-aware writes.
This commit is contained in:
@@ -456,7 +456,7 @@ class GatewayAuthorizationMixin:
|
||||
Platform.QQBOT: "QQ_GROUP_ALLOWED_USERS",
|
||||
}.get(source.platform, "")
|
||||
if chat_allowlist_env:
|
||||
raw_chat_allowlist = os.getenv(chat_allowlist_env, "").strip()
|
||||
raw_chat_allowlist = _platform_gate_env(chat_allowlist_env)
|
||||
if raw_chat_allowlist:
|
||||
allowed_group_ids = {
|
||||
cid.strip()
|
||||
@@ -498,7 +498,7 @@ class GatewayAuthorizationMixin:
|
||||
}
|
||||
if getattr(source, "is_bot", False):
|
||||
allow_bots_var = platform_allow_bots_map.get(source.platform)
|
||||
if allow_bots_var and os.getenv(allow_bots_var, "none").lower().strip() in {"mentions", "all"}:
|
||||
if allow_bots_var and _platform_gate_env(allow_bots_var, "none").lower().strip() in {"mentions", "all"}:
|
||||
return True
|
||||
|
||||
if not user_id:
|
||||
@@ -876,13 +876,13 @@ class GatewayAuthorizationMixin:
|
||||
),
|
||||
Platform.QQBOT: ("QQ_GROUP_ALLOWED_USERS",),
|
||||
}
|
||||
if os.getenv(platform_env_map.get(platform, ""), "").strip():
|
||||
if _platform_gate_env(platform_env_map.get(platform, "")).strip():
|
||||
return "ignore"
|
||||
for env_key in platform_group_env_map.get(platform, ()):
|
||||
if os.getenv(env_key, "").strip():
|
||||
if _platform_gate_env(env_key).strip():
|
||||
return "ignore"
|
||||
|
||||
if os.getenv("GATEWAY_ALLOWED_USERS", "").strip():
|
||||
if _platform_gate_env("GATEWAY_ALLOWED_USERS").strip():
|
||||
return "ignore"
|
||||
|
||||
return "pair"
|
||||
|
||||
+28
-2
@@ -146,6 +146,32 @@ def _user_ids_match(platform: str, left: str, right: str) -> bool:
|
||||
return bool(left_aliases and right_aliases and (left_aliases & right_aliases))
|
||||
|
||||
|
||||
def _read_allowlist_env(env_var: str) -> str:
|
||||
"""Read a platform allowlist env var through the profile secret scope.
|
||||
|
||||
Under multiplexing the process env may hold ANOTHER profile's allowlist
|
||||
(first-writer-wins YAML→env bridges), so reads must honor the installed
|
||||
scope's verdict — including a scoped miss returning empty rather than
|
||||
borrowing the process value. Unscoped callers (single-profile CLI /
|
||||
admin endpoints) keep the legacy ``os.getenv`` read.
|
||||
|
||||
TODO(profile-secrets): the grant mirror below still WRITES through
|
||||
``hermes_cli.config.save_env_value`` / ``remove_env_value``, which target
|
||||
the root ``.env`` — those writes need a profile-aware counterpart before
|
||||
pairing grants can be mirrored correctly under multiplexing.
|
||||
"""
|
||||
try:
|
||||
from agent.secret_scope import UnscopedSecretError, get_secret
|
||||
|
||||
try:
|
||||
return (get_secret(env_var) or "").strip()
|
||||
except UnscopedSecretError:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
return (os.getenv(env_var) or "").strip()
|
||||
|
||||
|
||||
def _sync_allowlist_add(platform: str, user_id: str) -> None:
|
||||
"""Add ``user_id`` to the platform allowlist env var IF one is configured.
|
||||
|
||||
@@ -158,7 +184,7 @@ def _sync_allowlist_add(platform: str, user_id: str) -> None:
|
||||
env_var = _allowlist_env_for_platform(platform)
|
||||
if not env_var:
|
||||
return
|
||||
current = os.getenv(env_var, "").strip()
|
||||
current = _read_allowlist_env(env_var)
|
||||
if not current:
|
||||
return # No allowlist configured — leave the gateway open (option i).
|
||||
ids = _split_allowlist(current)
|
||||
@@ -278,7 +304,7 @@ def _sync_allowlist_remove(platform: str, user_id: str) -> None:
|
||||
env_var = _allowlist_env_for_platform(platform)
|
||||
if not env_var:
|
||||
return
|
||||
current = os.getenv(env_var, "").strip()
|
||||
current = _read_allowlist_env(env_var)
|
||||
if not current:
|
||||
return # No allowlist configured — do not touch config-only snapshots.
|
||||
ids = _split_allowlist(current)
|
||||
|
||||
Reference in New Issue
Block a user