From 7a5acb29acc7ae883a2f405806b368275574919e Mon Sep 17 00:00:00 2001 From: Luca Simone Date: Sun, 17 Dec 2023 17:38:27 +0100 Subject: [PATCH 1/2] feat: Improved server, security and commands Signed-off-by: Luca Simone --- pr_agent/servers/bitbucket_server_webhook.py | 48 +++++++++++++------- pr_agent/settings/.secrets_template.toml | 5 ++ pr_agent/settings/configuration.toml | 5 ++ 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/pr_agent/servers/bitbucket_server_webhook.py b/pr_agent/servers/bitbucket_server_webhook.py index c6ce83536..eac9d5690 100644 --- a/pr_agent/servers/bitbucket_server_webhook.py +++ b/pr_agent/servers/bitbucket_server_webhook.py @@ -1,4 +1,5 @@ import json +import os import uvicorn from fastapi import APIRouter, FastAPI @@ -13,35 +14,55 @@ from pr_agent.agent.pr_agent import PRAgent from pr_agent.config_loader import get_settings from pr_agent.log import get_logger +from pr_agent.servers.utils import verify_signature router = APIRouter() -def handle_request(background_tasks: BackgroundTasks, url: str, body: str, log_context: dict): +def handle_request( + background_tasks: BackgroundTasks, url: str, body: str, log_context: dict +): log_context["action"] = body - log_context["event"] = "pull_request" if body == "review" else "comment" log_context["api_url"] = url with get_logger().contextualize(**log_context): background_tasks.add_task(PRAgent().handle_request, url, body) -@router.post("/webhook") +@router.post("/") async def handle_webhook(background_tasks: BackgroundTasks, request: Request): log_context = {"server_type": "bitbucket_server"} data = await request.json() get_logger().info(json.dumps(data)) - pr_id = data['pullRequest']['id'] - repository_name = data['pullRequest']['toRef']['repository']['slug'] - project_name = data['pullRequest']['toRef']['repository']['project']['key'] + webhook_secret = getattr(get_settings().bitbucket_server, "webhook_secret", None) + if webhook_secret: + body_bytes = await request.body() + signature_header = request.headers.get("x-hub-signature", None) + verify_signature(body_bytes, webhook_secret, signature_header) + + pr_id = data["pullRequest"]["id"] + repository_name = data["pullRequest"]["toRef"]["repository"]["slug"] + project_name = data["pullRequest"]["toRef"]["repository"]["project"]["key"] bitbucket_server = get_settings().get("BITBUCKET_SERVER.URL") pr_url = f"{bitbucket_server}/projects/{project_name}/repos/{repository_name}/pull-requests/{pr_id}" log_context["api_url"] = pr_url log_context["event"] = "pull_request" - handle_request(background_tasks, pr_url, "review", log_context) - return JSONResponse(status_code=status.HTTP_200_OK, content=jsonable_encoder({"message": "success"})) + if data["eventKey"] == "pr:opened": + body = "review" + elif data["eventKey"] == "pr:comment:added": + body = data["comment"]["text"] + else: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content=json.dumps({"message": "Unsupported event"}), + ) + + handle_request(background_tasks, pr_url, body, log_context) + return JSONResponse( + status_code=status.HTTP_200_OK, content=jsonable_encoder({"message": "success"}) + ) @router.get("/") @@ -50,15 +71,10 @@ async def root(): def start(): - bitbucket_server_url = get_settings().get("BITBUCKET_SERVER.URL", None) - if not bitbucket_server_url: - raise ValueError("BITBUCKET_SERVER.URL is not set") - get_settings().config.git_provider = "bitbucket_server" - middleware = [Middleware(RawContextMiddleware)] - app = FastAPI(middleware=middleware) + app = FastAPI(middleware=[Middleware(RawContextMiddleware)]) app.include_router(router) - uvicorn.run(app, host="0.0.0.0", port=3000) + uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "3000"))) -if __name__ == '__main__': +if __name__ == "__main__": start() diff --git a/pr_agent/settings/.secrets_template.toml b/pr_agent/settings/.secrets_template.toml index e7ca4057c..9ed806bee 100644 --- a/pr_agent/settings/.secrets_template.toml +++ b/pr_agent/settings/.secrets_template.toml @@ -65,6 +65,11 @@ personal_access_token = "" # For Bitbucket personal/repository bearer token bearer_token = "" +[bitbucket_server] +# For Bitbucket Server bearer token +auth_token = "" +webhook_secret = "" + # For Bitbucket app app_key = "" base_url = "" diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 259383d7e..b8da04a7d 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -158,6 +158,11 @@ polling_interval_seconds = 30 # token to authenticate in the patch server # patch_server_token = "" +[bitbucket_server] +# URL to the BitBucket Server instance +# url = "https://git.bitbucket.com" +url = "" + [litellm] #use_client = false From e25980f141631ca6c4bbb7616a18a507c748bb38 Mon Sep 17 00:00:00 2001 From: Luca Simone Date: Mon, 18 Dec 2023 14:58:25 +0100 Subject: [PATCH 2/2] fix: using the same get_settings convention --- pr_agent/servers/bitbucket_server_webhook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/servers/bitbucket_server_webhook.py b/pr_agent/servers/bitbucket_server_webhook.py index eac9d5690..74c2158a3 100644 --- a/pr_agent/servers/bitbucket_server_webhook.py +++ b/pr_agent/servers/bitbucket_server_webhook.py @@ -34,7 +34,7 @@ async def handle_webhook(background_tasks: BackgroundTasks, request: Request): data = await request.json() get_logger().info(json.dumps(data)) - webhook_secret = getattr(get_settings().bitbucket_server, "webhook_secret", None) + webhook_secret = get_settings().get("BITBUCKET_SERVER.WEBHOOK_SECRET", None) if webhook_secret: body_bytes = await request.body() signature_header = request.headers.get("x-hub-signature", None)