diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index b890f68649e..316bc154fa4 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -2342,10 +2342,12 @@ def _validate_messaging_env_value(platform_id: str, key: str, value: str) -> Non ) if key == "SLACK_ALLOWED_USERS": user_ids = [part.strip() for part in value.split(",")] + # "*" is the gateway's allow-all wildcard (see gateway/platforms/slack.py), + # so accept it as a valid entry alongside Slack member IDs (U.../W...). invalid = [ user_id for user_id in user_ids - if not user_id or not re.fullmatch(r"[UW][A-Z0-9]{2,}", user_id) + if user_id != "*" and (not user_id or not re.fullmatch(r"[UW][A-Z0-9]{2,}", user_id)) ] if invalid: raise HTTPException( diff --git a/tests/hermes_cli/test_web_server.py b/tests/hermes_cli/test_web_server.py index d44c789b3e3..d7a4dbcbbf9 100644 --- a/tests/hermes_cli/test_web_server.py +++ b/tests/hermes_cli/test_web_server.py @@ -1687,6 +1687,19 @@ class TestWebServerEndpoints: assert resp.status_code == 400 assert "member IDs" in resp.json()["detail"] + def test_update_messaging_platform_accepts_slack_allowed_users_wildcard(self): + # "*" is the gateway's allow-all wildcard (gateway/platforms/slack.py), + # so the dashboard must accept it rather than rejecting it as malformed. + from hermes_cli.config import load_env + + resp = self.client.put( + "/api/messaging/platforms/slack", + json={"env": {"SLACK_ALLOWED_USERS": "*"}}, + ) + + assert resp.status_code == 200 + assert load_env()["SLACK_ALLOWED_USERS"] == "*" + def test_messaging_platform_test_reports_missing_required_setup(self): resp = self.client.put("/api/messaging/platforms/discord", json={"enabled": True}) assert resp.status_code == 200 diff --git a/web/src/pages/ChannelsPage.tsx b/web/src/pages/ChannelsPage.tsx index 84791738a25..db56beb1925 100644 --- a/web/src/pages/ChannelsPage.tsx +++ b/web/src/pages/ChannelsPage.tsx @@ -76,7 +76,7 @@ function validateMessagingEnvField(field: MessagingPlatformEnvVar, value: string if (parts.some((part) => !part)) { return "Slack member IDs must be comma-separated without empty entries."; } - const invalid = parts.find((part) => !SLACK_MEMBER_ID_RE.test(part)); + const invalid = parts.find((part) => part !== "*" && !SLACK_MEMBER_ID_RE.test(part)); if (invalid) { return `${invalid} does not look like a Slack member ID. Use IDs like U01ABC2DEF3.`; }