quick fix
This commit is contained in:
@@ -29,6 +29,7 @@ LOG_LEVEL=INFO
|
|||||||
AUDIT_LOG_PATH=/var/log/aegis-mcp/audit.log
|
AUDIT_LOG_PATH=/var/log/aegis-mcp/audit.log
|
||||||
METRICS_ENABLED=true
|
METRICS_ENABLED=true
|
||||||
EXPOSE_ERROR_DETAILS=false
|
EXPOSE_ERROR_DETAILS=false
|
||||||
|
STARTUP_VALIDATE_GITEA=true
|
||||||
|
|
||||||
# Tool output limits
|
# Tool output limits
|
||||||
MAX_FILE_SIZE_BYTES=1048576
|
MAX_FILE_SIZE_BYTES=1048576
|
||||||
|
|||||||
@@ -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 |
|
| 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_PORT` | No | `8080` | Port to listen on |
|
||||||
| `MCP_DOMAIN` | No | — | Public domain name (used for Traefik labels in Docker) |
|
| `MCP_DOMAIN` | No | — | Public domain name (used for Traefik labels in Docker) |
|
||||||
| `LOG_LEVEL` | No | `INFO` | Log level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
|
| `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
|
GITEA_TOKEN=abcdef1234567890abcdef1234567890
|
||||||
|
|
||||||
# Server
|
# Server
|
||||||
MCP_HOST=0.0.0.0
|
MCP_HOST=127.0.0.1
|
||||||
MCP_PORT=8080
|
MCP_PORT=8080
|
||||||
MCP_DOMAIN=mcp.example.com
|
MCP_DOMAIN=mcp.example.com
|
||||||
LOG_LEVEL=INFO
|
LOG_LEVEL=INFO
|
||||||
|
STARTUP_VALIDATE_GITEA=true
|
||||||
|
|
||||||
# Auth
|
# Auth
|
||||||
AUTH_ENABLED=true
|
AUTH_ENABLED=true
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ make run
|
|||||||
# or: python -m aegis_gitea_mcp.server
|
# 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:
|
Verify it is running:
|
||||||
|
|
||||||
|
|||||||
@@ -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.auth import get_validator
|
||||||
from aegis_gitea_mcp.automation import AutomationError, AutomationManager
|
from aegis_gitea_mcp.automation import AutomationError, AutomationManager
|
||||||
from aegis_gitea_mcp.config import get_settings
|
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.logging_utils import configure_logging
|
||||||
from aegis_gitea_mcp.mcp_protocol import (
|
from aegis_gitea_mcp.mcp_protocol import (
|
||||||
AVAILABLE_TOOLS,
|
AVAILABLE_TOOLS,
|
||||||
@@ -240,9 +244,19 @@ async def startup_event() -> None:
|
|||||||
async with GiteaClient() as gitea:
|
async with GiteaClient() as gitea:
|
||||||
user = await gitea.get_current_user()
|
user = await gitea.get_current_user()
|
||||||
logger.info("gitea_connected", extra={"bot_user": user.get("login", "unknown")})
|
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")
|
logger.error("gitea_connection_failed")
|
||||||
raise
|
raise RuntimeError("Startup validation failed: unable to connect to Gitea.") from exc
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("shutdown")
|
@app.on_event("shutdown")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from fastapi.testclient import TestClient
|
|||||||
|
|
||||||
from aegis_gitea_mcp.auth import reset_validator
|
from aegis_gitea_mcp.auth import reset_validator
|
||||||
from aegis_gitea_mcp.config import reset_settings
|
from aegis_gitea_mcp.config import reset_settings
|
||||||
|
from aegis_gitea_mcp.gitea_client import GiteaAuthenticationError, GiteaAuthorizationError
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
@@ -238,3 +239,50 @@ def test_rate_limiting(client):
|
|||||||
# Last response should mention rate limiting
|
# Last response should mention rate limiting
|
||||||
data = response.json()
|
data = response.json()
|
||||||
assert "Too many failed" in data["message"]
|
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()
|
||||||
|
|||||||
Reference in New Issue
Block a user