From ecc87cbb65cdac8bc1f326e31ea1677e889dce93 Mon Sep 17 00:00:00 2001 From: latte Date: Sat, 14 Feb 2026 17:18:30 +0100 Subject: [PATCH] quick fix --- .env.example | 1 + docs/configuration.md | 8 ++++-- docs/getting-started.md | 2 +- src/aegis_gitea_mcp/server.py | 20 ++++++++++++--- tests/test_server.py | 48 +++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 69c075b..d1b81c2 100644 --- a/.env.example +++ b/.env.example @@ -29,6 +29,7 @@ LOG_LEVEL=INFO AUDIT_LOG_PATH=/var/log/aegis-mcp/audit.log METRICS_ENABLED=true EXPOSE_ERROR_DETAILS=false +STARTUP_VALIDATE_GITEA=true # Tool output limits MAX_FILE_SIZE_BYTES=1048576 diff --git a/docs/configuration.md b/docs/configuration.md index 104f538..b2c337f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -23,10 +23,13 @@ The `GITEA_TOKEN` must be a token belonging to a user that has at least read acc | Variable | Required | Default | Description | |---|---|---|---| -| `MCP_HOST` | No | `0.0.0.0` | Interface to bind to | +| `MCP_HOST` | No | `127.0.0.1` | Interface to bind to | | `MCP_PORT` | No | `8080` | Port to listen on | | `MCP_DOMAIN` | No | — | Public domain name (used for Traefik labels in Docker) | | `LOG_LEVEL` | No | `INFO` | Log level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` | +| `STARTUP_VALIDATE_GITEA` | No | `true` | Validate Gitea token and connectivity at startup via `/api/v1/user` | + +If startup validation fails with `403 Forbidden`, the token is authenticated but lacks permission to access `/api/v1/user`. Grant the bot user token the required API scope/permissions, or temporarily set `STARTUP_VALIDATE_GITEA=false` in controlled troubleshooting environments. --- @@ -84,10 +87,11 @@ GITEA_URL=https://gitea.example.com GITEA_TOKEN=abcdef1234567890abcdef1234567890 # Server -MCP_HOST=0.0.0.0 +MCP_HOST=127.0.0.1 MCP_PORT=8080 MCP_DOMAIN=mcp.example.com LOG_LEVEL=INFO +STARTUP_VALIDATE_GITEA=true # Auth AUTH_ENABLED=true diff --git a/docs/getting-started.md b/docs/getting-started.md index d345713..974b61e 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -77,7 +77,7 @@ make run # or: python -m aegis_gitea_mcp.server ``` -The server starts on `http://0.0.0.0:8080` by default. +The server starts on `http://127.0.0.1:8080` by default. Verify it is running: diff --git a/src/aegis_gitea_mcp/server.py b/src/aegis_gitea_mcp/server.py index c429e02..6e136c9 100644 --- a/src/aegis_gitea_mcp/server.py +++ b/src/aegis_gitea_mcp/server.py @@ -17,7 +17,11 @@ from aegis_gitea_mcp.audit import get_audit_logger from aegis_gitea_mcp.auth import get_validator from aegis_gitea_mcp.automation import AutomationError, AutomationManager from aegis_gitea_mcp.config import get_settings -from aegis_gitea_mcp.gitea_client import GiteaClient +from aegis_gitea_mcp.gitea_client import ( + GiteaAuthenticationError, + GiteaAuthorizationError, + GiteaClient, +) from aegis_gitea_mcp.logging_utils import configure_logging from aegis_gitea_mcp.mcp_protocol import ( AVAILABLE_TOOLS, @@ -240,9 +244,19 @@ async def startup_event() -> None: async with GiteaClient() as gitea: user = await gitea.get_current_user() logger.info("gitea_connected", extra={"bot_user": user.get("login", "unknown")}) - except Exception: + except GiteaAuthenticationError as exc: + logger.error("gitea_connection_failed_authentication") + raise RuntimeError( + "Startup validation failed: Gitea authentication was rejected. Check GITEA_TOKEN." + ) from exc + except GiteaAuthorizationError as exc: + logger.error("gitea_connection_failed_authorization") + raise RuntimeError( + "Startup validation failed: Gitea token lacks permission for /api/v1/user." + ) from exc + except Exception as exc: logger.error("gitea_connection_failed") - raise + raise RuntimeError("Startup validation failed: unable to connect to Gitea.") from exc @app.on_event("shutdown") diff --git a/tests/test_server.py b/tests/test_server.py index d06c564..59e806c 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -5,6 +5,7 @@ from fastapi.testclient import TestClient from aegis_gitea_mcp.auth import reset_validator from aegis_gitea_mcp.config import reset_settings +from aegis_gitea_mcp.gitea_client import GiteaAuthenticationError, GiteaAuthorizationError @pytest.fixture(autouse=True) @@ -238,3 +239,50 @@ def test_rate_limiting(client): # Last response should mention rate limiting data = response.json() assert "Too many failed" in data["message"] + + +@pytest.mark.asyncio +async def test_startup_event_fails_with_authentication_guidance(monkeypatch): + """Startup validation should fail with explicit auth guidance on 401.""" + monkeypatch.setenv("GITEA_URL", "https://gitea.example.com") + monkeypatch.setenv("GITEA_TOKEN", "test-gitea-token-12345") + monkeypatch.setenv("ENVIRONMENT", "production") + monkeypatch.setenv("AUTH_ENABLED", "true") + monkeypatch.setenv("MCP_API_KEYS", "a" * 64) + monkeypatch.setenv("STARTUP_VALIDATE_GITEA", "true") + + from aegis_gitea_mcp import server + + async def raise_auth_error(*_args, **_kwargs): + raise GiteaAuthenticationError("Authentication failed - check bot token") + + monkeypatch.setattr(server.GiteaClient, "get_current_user", raise_auth_error) + + with pytest.raises( + RuntimeError, match=r"Startup validation failed: Gitea authentication was rejected" + ): + await server.startup_event() + + +@pytest.mark.asyncio +async def test_startup_event_fails_with_authorization_guidance(monkeypatch): + """Startup validation should fail with explicit permission guidance on 403.""" + monkeypatch.setenv("GITEA_URL", "https://gitea.example.com") + monkeypatch.setenv("GITEA_TOKEN", "test-gitea-token-12345") + monkeypatch.setenv("ENVIRONMENT", "production") + monkeypatch.setenv("AUTH_ENABLED", "true") + monkeypatch.setenv("MCP_API_KEYS", "a" * 64) + monkeypatch.setenv("STARTUP_VALIDATE_GITEA", "true") + + from aegis_gitea_mcp import server + + async def raise_authorization_error(*_args, **_kwargs): + raise GiteaAuthorizationError("Bot user lacks permission for this operation") + + monkeypatch.setattr(server.GiteaClient, "get_current_user", raise_authorization_error) + + with pytest.raises( + RuntimeError, + match=r"Startup validation failed: Gitea token lacks permission for /api/v1/user", + ): + await server.startup_event()