Remove command permission lookup
This commit is contained in:
@@ -15,9 +15,9 @@ one persistent Docker Compose service on the same Docker network as Gitea.
|
|||||||
| PR | `/agent iterate [message]` | Resume an agent implementation and its reviewer once. |
|
| PR | `/agent iterate [message]` | Resume an agent implementation and its reviewer once. |
|
||||||
| PR | `/agent fix [message]` | Start a fresh one-shot fix session and push one commit. |
|
| PR | `/agent fix [message]` | Start a fresh one-shot fix session and push one commit. |
|
||||||
|
|
||||||
Only repository writers and administrators can enqueue commands. Each command
|
Anyone who can comment on an issue or pull request can enqueue commands. Each
|
||||||
gets separate queued and started comments. Final plans, PR results, failures,
|
command gets separate queued and started comments. Final plans, PR results,
|
||||||
and remaining review findings are posted separately.
|
failures, and remaining review findings are posted separately.
|
||||||
|
|
||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
@@ -83,4 +83,3 @@ uv run pytest
|
|||||||
|
|
||||||
The tests fail if any tracked Python file exceeds 250 lines. Prompts and JSON
|
The tests fail if any tracked Python file exceeds 250 lines. Prompts and JSON
|
||||||
schemas live outside Python so orchestration modules remain small and readable.
|
schemas live outside Python so orchestration modules remain small and readable.
|
||||||
|
|
||||||
|
|||||||
@@ -34,13 +34,6 @@ class GiteaClient:
|
|||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
await self.client.aclose()
|
await self.client.aclose()
|
||||||
|
|
||||||
async def has_write_permission(self, owner: str, repo: str, username: str) -> bool:
|
|
||||||
response = await self._request(
|
|
||||||
"GET", f"/repos/{owner}/{repo}/collaborators/{username}/permission"
|
|
||||||
)
|
|
||||||
permission = str(response.json().get("permission", "")).lower()
|
|
||||||
return permission in {"write", "admin", "owner"}
|
|
||||||
|
|
||||||
async def repository(self, owner: str, repo: str) -> RepositoryInfo:
|
async def repository(self, owner: str, repo: str) -> RepositoryInfo:
|
||||||
data = (await self._request("GET", f"/repos/{owner}/{repo}")).json()
|
data = (await self._request("GET", f"/repos/{owner}/{repo}")).json()
|
||||||
return RepositoryInfo(
|
return RepositoryInfo(
|
||||||
|
|||||||
@@ -70,19 +70,6 @@ async def _handle_command(container: Any, event: CommandEvent) -> Response:
|
|||||||
if not event.body.strip().startswith("/agent"):
|
if not event.body.strip().startswith("/agent"):
|
||||||
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
||||||
log.info("agent command received", extra=extra)
|
log.info("agent command received", extra=extra)
|
||||||
permitted = await container.gitea.has_write_permission(
|
|
||||||
event.repo_owner, event.repo_name, event.requester
|
|
||||||
)
|
|
||||||
if not permitted:
|
|
||||||
log.warning("agent command rejected: insufficient permission", extra=extra)
|
|
||||||
if await container.storage.record_delivery(event.delivery_id, event.comment_id):
|
|
||||||
await container.gitea.create_comment(
|
|
||||||
event.repo_owner,
|
|
||||||
event.repo_name,
|
|
||||||
event.issue_number,
|
|
||||||
"Agent command rejected: repository write permission is required.",
|
|
||||||
)
|
|
||||||
return Response(status_code=status.HTTP_202_ACCEPTED)
|
|
||||||
try:
|
try:
|
||||||
command = parse_command(event.body)
|
command = parse_command(event.body)
|
||||||
except CommandError as exc:
|
except CommandError as exc:
|
||||||
|
|||||||
+3
-24
@@ -29,13 +29,9 @@ class FakeStorage:
|
|||||||
|
|
||||||
|
|
||||||
class FakeGitea:
|
class FakeGitea:
|
||||||
def __init__(self, permitted: bool = True) -> None:
|
def __init__(self) -> None:
|
||||||
self.permitted = permitted
|
|
||||||
self.comments: list[str] = []
|
self.comments: list[str] = []
|
||||||
|
|
||||||
async def has_write_permission(self, *_args):
|
|
||||||
return self.permitted
|
|
||||||
|
|
||||||
async def create_comment(self, _owner, _repo, _number, body):
|
async def create_comment(self, _owner, _repo, _number, body):
|
||||||
self.comments.append(body)
|
self.comments.append(body)
|
||||||
return len(self.comments)
|
return len(self.comments)
|
||||||
@@ -76,25 +72,8 @@ async def test_authorized_command_is_queued() -> None:
|
|||||||
assert "queued" in gitea.comments[0]
|
assert "queued" in gitea.comments[0]
|
||||||
|
|
||||||
|
|
||||||
async def test_unauthorized_command_is_rejected_and_deduplicated() -> None:
|
async def test_non_command_is_ignored() -> None:
|
||||||
storage = FakeStorage()
|
container = SimpleNamespace(storage=FakeStorage(), gitea=FakeGitea())
|
||||||
gitea = FakeGitea(permitted=False)
|
|
||||||
container = SimpleNamespace(storage=storage, gitea=gitea)
|
|
||||||
event = _event_from_payload("delivery", payload("/agent implement"))
|
|
||||||
assert event is not None
|
|
||||||
await _handle_command(container, event)
|
|
||||||
await _handle_command(container, event)
|
|
||||||
assert storage.jobs == []
|
|
||||||
assert len(gitea.comments) == 1
|
|
||||||
assert "write permission" in gitea.comments[0]
|
|
||||||
|
|
||||||
|
|
||||||
async def test_non_command_does_not_query_permission() -> None:
|
|
||||||
class ExplodingGitea(FakeGitea):
|
|
||||||
async def has_write_permission(self, *_args):
|
|
||||||
raise AssertionError("permission lookup should not run")
|
|
||||||
|
|
||||||
container = SimpleNamespace(storage=FakeStorage(), gitea=ExplodingGitea())
|
|
||||||
event = _event_from_payload("delivery", payload("ordinary discussion"))
|
event = _event_from_payload("delivery", payload("ordinary discussion"))
|
||||||
assert event is not None
|
assert event is not None
|
||||||
response = await _handle_command(container, event)
|
response = await _handle_command(container, event)
|
||||||
|
|||||||
Reference in New Issue
Block a user