From 30a982c95d279c26e93f7874470a14c8df4ded04 Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Fri, 1 May 2026 15:12:16 +0200 Subject: [PATCH] Add middleware for Gitea webhook signatures --- docs/CHANGELOG.md | 3 +- .../gitea/middlewares/__init__.py | 19 ++++++ .../gitea/middlewares/signature.py | 62 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/jcloud_deployment_server/integrations/gitea/middlewares/__init__.py create mode 100644 src/jcloud_deployment_server/integrations/gitea/middlewares/signature.py diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d15afe4..1b1efc9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,4 +6,5 @@ - Add basic API structure - Add configuration file template -- Add models for Gitea webhooks \ No newline at end of file +- Add models for Gitea webhooks +- Add middleware for Gitea webhook signatures \ No newline at end of file diff --git a/src/jcloud_deployment_server/integrations/gitea/middlewares/__init__.py b/src/jcloud_deployment_server/integrations/gitea/middlewares/__init__.py new file mode 100644 index 0000000..41858f3 --- /dev/null +++ b/src/jcloud_deployment_server/integrations/gitea/middlewares/__init__.py @@ -0,0 +1,19 @@ +# Copyright 2026 jCloud + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from . import signature + +__all__ = [ + 'signature' +] \ No newline at end of file diff --git a/src/jcloud_deployment_server/integrations/gitea/middlewares/signature.py b/src/jcloud_deployment_server/integrations/gitea/middlewares/signature.py new file mode 100644 index 0000000..9eab7db --- /dev/null +++ b/src/jcloud_deployment_server/integrations/gitea/middlewares/signature.py @@ -0,0 +1,62 @@ +# Copyright 2026 jCloud + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import hmac +import hashlib +from fastapi import Request, HTTPException +from starlette.middleware.base import BaseHTTPMiddleware + +__all__ = [ + 'GiteaSignatureMiddleware' +] + +class GiteaSignatureMiddleware(BaseHTTPMiddleware): + ''' + A middleware to verify the Gitea signature. + + :param app: The (FastAPI) app. + :param secret: The secret. + :type secret: bytes + ''' + def __init__(self, app, secret: bytes) -> None: + super().__init__(app) + self.secret = secret + + async def dispatch(self, request: Request, call_next): + ''' + Dispatch. + + :param request: The request. + :type request: Request + ''' + if request.url.path.startswith('/webhook/gitea'): + + signature = request.headers.get('X-Gitea-Signature') + + if not signature: + raise HTTPException(status_code = 401, detail = 'Missing signature') + + body = await request.body() + + if not hmac.compare_digest( + f'sha256={hmac.new( + self.secret, + body, + hashlib.sha256 + ).digest()}', + signature + ): + raise HTTPException(status_code = 401, detail = 'Invalid signature') + + return await call_next(request) \ No newline at end of file