feat: scope list_repositories to the authenticated user in service-PAT mode
Previously list_repositories was blocked in service-PAT mode because it has no repository target for the per-user permission check, so users could not list their repositories at all (the connector surfaced a generic error). list_repositories now returns only the repositories the signed-in user owns or contributes to, instead of everything the bot token can see: - gitea_client.py: add list_user_repositories(login) — resolves the user id and queries /api/v1/repos/search with the uid filter. - repository.py: list_repositories_tool uses the user-scoped path when a service PAT is configured and a user login is present; pure-OAuth mode still uses the user's own /user/repos. - server.py: allow list_repositories through the service-PAT guard (it is scoped to the user in the handler); all other tools still require a repository target. - README.md: document the new user-scoped behavior and its visibility caveat. Tests: user-scoped client method (uid resolution + unknown user), PAT-mode tool scoping, and conftest now clears the request context between tests to prevent contextvar login leakage across files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ from aegis_gitea_mcp.gitea_client import (
|
||||
GiteaAuthorizationError,
|
||||
GiteaError,
|
||||
)
|
||||
from aegis_gitea_mcp.request_context import clear_gitea_auth_context, set_gitea_user_login
|
||||
from aegis_gitea_mcp.tools.repository import (
|
||||
get_file_contents_tool,
|
||||
get_file_tree_tool,
|
||||
@@ -91,6 +92,28 @@ async def test_tool_propagates_auth_errors_unwrapped(auth_error: type[GiteaError
|
||||
await list_repositories_tool(AuthErrorStub(), {})
|
||||
|
||||
|
||||
class UserScopedStub(RepoStub):
|
||||
"""Stub exposing the per-user listing path used in service-PAT mode."""
|
||||
|
||||
async def list_user_repositories(self, login):
|
||||
return [{"name": "mine", "owner": {"login": login}, "full_name": f"{login}/mine"}]
|
||||
|
||||
async def list_repositories(self):
|
||||
raise AssertionError("PAT mode with a known user must use the user-scoped listing")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_repositories_tool_scopes_to_user_in_pat_mode() -> None:
|
||||
"""With a service PAT (GITEA_TOKEN) and a known user, listing is user-scoped."""
|
||||
set_gitea_user_login("alice")
|
||||
try:
|
||||
result = await list_repositories_tool(UserScopedStub(), {})
|
||||
finally:
|
||||
clear_gitea_auth_context()
|
||||
assert result["count"] == 1
|
||||
assert result["repositories"][0]["full_name"] == "alice/mine"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_repository_info_tool_success() -> None:
|
||||
"""Repository info tool returns normalized metadata."""
|
||||
|
||||
Reference in New Issue
Block a user