-
-
Notifications
You must be signed in to change notification settings - Fork 3
chore: drop generated artifacts not tracked on master #231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -621,6 +621,7 @@ def _make_handler(data_dir, runner: _ServiceLoop, verifier=None, | |
| _registry_url, | ||
| revoked_token=_registry_admin_token, | ||
| expected_iss=registry_auth.REGISTRY_ISS, | ||
| human_iss=registry_auth.CONTROLLER_ISS, | ||
| ) | ||
| _grants_verifier = registry_auth.grants_verifier_from_url( | ||
| _registry_url, | ||
|
|
@@ -1446,14 +1447,16 @@ def _handle_a2a_send(self) -> None: | |
| # 401/403. In verify-and-warn mode (default) failures are logged as | ||
| # a WARNING but the message is accepted, allowing operators to observe | ||
| # violations before enabling hard enforcement. | ||
| # Human principals (sub starting with user-) are always rejected on | ||
| # auth failure regardless of mode: missing credential is the only | ||
| # tolerated class during migration. | ||
| sender = from_ | ||
| _is_human = False | ||
| if _registry_verifier is not None: | ||
| from . import registry_auth # noqa: PLC0415 - optional path | ||
| auth = self.headers.get("Authorization", "") | ||
| token = auth[len("Bearer "):].strip() if auth.startswith("Bearer ") else "" | ||
|
|
||
| # Compute warn_reason (None = auth passed) and the status/message | ||
| # to use in enforce mode. We collect these without returning early | ||
| # so the enforce vs. warn decision is made in one place below. | ||
| warn_reason: str | None = None | ||
| _reject_status: int = 403 | ||
| _reject_msg: str = "" | ||
|
|
@@ -1464,19 +1467,24 @@ def _handle_a2a_send(self) -> None: | |
| _reject_msg = "registry auth: Bearer token required" | ||
| else: | ||
| try: | ||
| _registry_verifier.authorize(token, from_) | ||
| claims = _registry_verifier.authorize(token, from_) | ||
| sender = claims["sub"] | ||
| _is_human = registry_auth._is_human_sub(sender) | ||
| except registry_auth.AuthError as exc: | ||
| warn_reason = str(exc) | ||
| _reject_status = 403 | ||
| _reject_msg = f"registry auth: {exc}" | ||
| # Presented-but-failing credentials are always rejected | ||
| # (both modes), regardless of principal type. Missing | ||
| # credential is the only class tolerated during migration. | ||
| self._send_json(403, {"error": f"registry auth: {exc}"}) | ||
| return | ||
|
Comment on lines
+1474
to
+1478
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Autherror return skips archiving _handle_a2a_send() now returns immediately on presented-but-invalid credentials, so the request is rejected without any archive.record() call. This violates the requirement to archive interactions on both success and failure paths (including errors). Agent Prompt
Comment on lines
+1475
to
+1478
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2. Warn-mode docs stale taosmd/http_server.py documents verify-and-warn mode as logging auth failures and still accepting messages, but _handle_a2a_send now returns 403 immediately for presented-but-invalid credentials (skipping the warn/enforce decision and any warning log). This makes operator guidance inaccurate and removes warn-mode visibility into invalid credential attempts. Agent Prompt
|
||
|
|
||
| # Grant check: token proves identity; grant proves permission. | ||
| if warn_reason is None and _grants_verifier is not None: | ||
| # Humans are not in the registry, so they have no grants. | ||
| if warn_reason is None and _grants_verifier is not None and not _is_human: | ||
| try: | ||
| if not _grants_verifier.has_grant(from_): | ||
| if not _grants_verifier.has_grant(sender): | ||
| warn_reason = "no a2a_send grant" | ||
| _reject_status = 403 | ||
| _reject_msg = f"registry auth: no active grant for {from_!r}" | ||
| _reject_msg = f"registry auth: no active grant for {sender!r}" | ||
|
Comment on lines
+1470
to
+1487
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win Avoid calling a module-private helper across module boundaries.
♻️ Proposed rename to a public helper-def _is_human_sub(sub: str) -> bool:
+def is_human_sub(sub: str) -> bool:
"""Return True if the sub is a human canonical id (user-* convention)."""
return sub.startswith("user-")Update the internal call site in 🤖 Prompt for AI Agents
Comment on lines
+1482
to
+1487
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Check whether human principals (user-* subjects) are expected to reach
# endpoints gated by _apply_token_binding, and whether has_grant is exempted for them.
rg -n -C5 '_apply_token_binding|has_grant\(' taosmd/http_server.pyRepository: jaylfc/taosmd Length of output: 9483 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== http_server.py relevant token/human binding sections =="
sed -n '730,875p' taosmd/http_server.py | cat -n | sed 's/^/730+/'
echo
echo "== occurrences of _is_human_sub / user- / human principals =="
rg -n -C3 '_is_human_sub|user-_|is_human|human|_token_project|_apply_token_binding' taosmd/http_server.py
echo
echo "== route handlers mentioning /ingest /search /tasks =="
rg -n -C4 '"/(ingest|search|tasks|tasks/|ingest_batch)|ingest_batch|task_list|task_ready|task_prime|task_edges|_handle_task' taosmd/http_server.py | sed -n '1,240p'Repository: jaylfc/taosmd Length of output: 27628 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== registry_auth.py outline =="
ast-grep outline taosmd/registry_auth.py --view expanded || true
echo
echo "== registry_auth.py relevant constants and _is_human_sub implementation =="
rg -n -C8 'REGISTRY_ISS|CONTROLLER_ISS|_is_human_sub|has_grant|verify|decode' taosmd/registry_auth.py
echo
echo "== route auth gate coverage =="
sed -n '888,1002p' taosmd/http_server.py | cat -n | sed 's/^/888+/'Repository: jaylfc/taosmd Length of output: 16532 Keep human principals out of registry-gated data endpoints. Human principals are allowed on A2A because they are not registry grant members. Route them through admin-only auth instead of letting controller human tokens reach 🤖 Prompt for AI Agents |
||
| except registry_auth.AuthError as exc: | ||
| warn_reason = str(exc) | ||
| _reject_status = 403 | ||
|
|
@@ -1489,11 +1497,11 @@ def _handle_a2a_send(self) -> None: | |
| return | ||
| logger.warning( | ||
| "a2a verify-and-warn: accepting unverified post from %r: %s", | ||
| from_, warn_reason, | ||
| sender, warn_reason, | ||
| ) | ||
| result = runner.run( | ||
| service.a2a_send( | ||
| sender=from_, body=body_text, | ||
| sender=sender, body=body_text, | ||
| thread=thread, reply_to=reply_to, | ||
| refs=refs, blocks=blocks, | ||
| data_dir=data_dir, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,6 +105,28 @@ def fake_opener(url, timeout=5.0, token=None): | |
| return verifier, gv | ||
|
|
||
|
|
||
| def _make_human_verifier(): | ||
| """Build (registry_verifier, grants_verifier) pair configured for humans.""" | ||
| def fake_opener(url, timeout=5.0, token=None): | ||
| if url.endswith(registry_auth.PUBKEY_PATH): | ||
| return json.dumps({"pubkey": PUB_PEM}) | ||
| if url.endswith(registry_auth.REVOKED_PATH): | ||
| return json.dumps([]) | ||
| if url.endswith(registry_auth.GRANTS_PATH): | ||
| return json.dumps({"grants": []}) | ||
| raise ValueError(f"unexpected url: {url}") | ||
|
|
||
| verifier = registry_auth.verifier_from_url( | ||
| "http://reg.test", opener=fake_opener, | ||
| expected_iss=registry_auth.REGISTRY_ISS, | ||
| human_iss=registry_auth.CONTROLLER_ISS, | ||
| ) | ||
| gv = registry_auth.grants_verifier_from_url( | ||
| "http://reg.test", opener=fake_opener, | ||
| ) | ||
| return verifier, gv | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def warn_server(tmp_path, monkeypatch): | ||
| """Server with verifiers wired in but a2a_auth_enforce NOT set (default=False). | ||
|
|
@@ -173,12 +195,11 @@ def test_warn_no_token_accepted(warn_server, caplog): | |
|
|
||
|
|
||
| def test_warn_invalid_token_accepted(warn_server, caplog): | ||
| """Invalid token: message accepted and warning logged in warn mode.""" | ||
| """Invalid token: message rejected with 403 regardless of mode.""" | ||
| import logging | ||
| with caplog.at_level(logging.WARNING, logger="taosmd.http_server"): | ||
| status, body = _post_send(warn_server, "any-agent", "hello", token="not-a-jwt") | ||
| assert status == 200, body | ||
| assert any("verify-and-warn" in r.message for r in caplog.records) | ||
| assert status == 403, body | ||
|
Comment on lines
197
to
+202
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Rename the test to match its new assertion. The function is named 🧰 Tools🪛 Ruff (0.16.0)[error] 201-201: Possible hardcoded password assigned to argument: "token" (S106) 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| def test_warn_valid_token_no_grant_accepted(warn_server, caplog): | ||
|
|
@@ -330,3 +351,84 @@ def test_api_still_up_when_dashboard_hidden(tmp_path, monkeypatch): | |
| finally: | ||
| httpd.shutdown() | ||
| httpd.service_loop.close() | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Human principal support (unified-chat slice 3) | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| @pytest.fixture | ||
| def human_warn_server(tmp_path, monkeypatch): | ||
| """Server with human-capable verifier, a2a_auth_enforce NOT set (default). | ||
|
|
||
| Auth failures are rejected 403 in both modes (human policy). | ||
| """ | ||
| data_dir = tmp_path / "data" | ||
| data_dir.mkdir() | ||
| monkeypatch.setattr(taosmd_api, "_stores_cache", {}) | ||
|
|
||
| verifier, gv = _make_human_verifier() | ||
| httpd = http_server.make_server( | ||
| "127.0.0.1", 0, data_dir=str(data_dir), | ||
| verifier=verifier, grants_verifier=gv, | ||
| ) | ||
| httpd.service_loop.run(taosmd_api._ensure_stores(str(data_dir))) | ||
| host, port = httpd.server_address[:2] | ||
| thread = threading.Thread(target=httpd.serve_forever, daemon=True) | ||
| thread.start() | ||
| try: | ||
| yield f"http://{host}:{port}" | ||
| finally: | ||
| httpd.shutdown() | ||
| httpd.service_loop.close() | ||
|
|
||
|
|
||
| def test_human_assertion_sub_mismatch_rejected_in_warn_mode(human_warn_server): | ||
| """Human assertion whose sub does not match from is rejected 403 even in | ||
| warn mode (fail-first: on master this would be 200).""" | ||
| token = pyjwt.encode( | ||
| {"sub": "user-123", "iss": registry_auth.CONTROLLER_ISS}, | ||
| PRIV_PEM, algorithm="EdDSA", | ||
| ) | ||
| status, body = _post_send(human_warn_server, "user-456", "hello", token=token) | ||
| assert status == 403 | ||
|
|
||
|
|
||
| def test_human_assertion_sub_match_accredited(human_warn_server): | ||
| """Valid human assertion with matching sub is accepted.""" | ||
| token = pyjwt.encode( | ||
| {"sub": "user-123", "iss": registry_auth.CONTROLLER_ISS}, | ||
| PRIV_PEM, algorithm="EdDSA", | ||
| ) | ||
| status, body = _post_send(human_warn_server, "user-123", "hello", token=token) | ||
| assert status == 200, body | ||
|
|
||
|
|
||
| def test_agent_jwt_claiming_human_id_rejected_in_warn_mode(human_warn_server): | ||
| """An agent JWT (iss=taos-registry) with a user-* sub is rejected 403.""" | ||
| token = pyjwt.encode( | ||
| {"sub": "user-123", "iss": registry_auth.REGISTRY_ISS}, | ||
| PRIV_PEM, algorithm="EdDSA", | ||
| ) | ||
| status, body = _post_send(human_warn_server, "user-123", "hello", token=token) | ||
| assert status == 403 | ||
|
|
||
|
|
||
| def test_human_assertion_claiming_agent_id_rejected_in_warn_mode(human_warn_server): | ||
| """A human assertion (iss=taos-controller) with an agent sub is rejected 403.""" | ||
| token = pyjwt.encode( | ||
| {"sub": "agent-1", "iss": registry_auth.CONTROLLER_ISS}, | ||
| PRIV_PEM, algorithm="EdDSA", | ||
| ) | ||
| status, body = _post_send(human_warn_server, "agent-1", "hello", token=token) | ||
| assert status == 403 | ||
|
|
||
|
|
||
| def test_human_missing_token_accepted_in_warn_mode(human_warn_server, caplog): | ||
| """Missing token for a human principal is accepted with warning in warn mode.""" | ||
| import logging | ||
| with caplog.at_level(logging.WARNING, logger="taosmd.http_server"): | ||
| status, body = _post_send(human_warn_server, "user-123", "hello") | ||
| assert status == 200, body | ||
| assert any("verify-and-warn" in r.message and "missing Bearer token" in r.message | ||
| for r in caplog.records) | ||
|
Comment on lines
+386
to
+434
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Logic is correct; clean up unused unpacked variables. The four new tests correctly exercise sub mismatch, matching-sub acceptance, cross-principal issuer rejection in both directions, and missing-token tolerance. Static analysis flags 🧹 Proposed fix for unused unpacked variables- status, body = _post_send(human_warn_server, "user-456", "hello", token=token)
+ status, _body = _post_send(human_warn_server, "user-456", "hello", token=token)
assert status == 403Apply the same change at Lines 413 and 423. 🧰 Tools🪛 Ruff (0.16.0)[warning] 393-393: Unpacked variable Prefix it with an underscore or any other dummy variable pattern (RUF059) [warning] 413-413: Unpacked variable Prefix it with an underscore or any other dummy variable pattern (RUF059) [warning] 423-423: Unpacked variable Prefix it with an underscore or any other dummy variable pattern (RUF059) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Update the comment to match the generalized rejection policy.
The comment states: Human principals (sub starting with user-) are always rejected on auth failure regardless of mode: missing credential is the only tolerated class during migration. The actual implementation, per the comment further down, applies this rule to every principal type: Presented-but-failing credentials are always rejected (both modes), regardless of principal type. Missing credential is the only class tolerated during migration.
Reword the comment at Line 1450 so it does not imply the always-reject rule is human-specific. A future maintainer reading only this comment could reintroduce a warn-mode tolerance for agents with invalid tokens.
🤖 Prompt for AI Agents