Last active
February 11, 2021 22:40
-
-
Save bialesdaniel/12f2ce1a5a5802ae5e099a7476d81087 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import hmac | |
import logging | |
from rest_framework.viewsets import ViewSet | |
from rest_framework.parsers import JSONParser | |
from rest_framework.response import Response | |
from rest_framework.status import HTTP_200_OK, HTTP_500_INTERNAL_SERVER_ERROR, HTTP_403_FORBIDDEN, HTTP_400_BAD_REQUEST | |
from ..RepositoryClient import RepositoryClient | |
from ..PerformanceGuardBot import PerformanceGuardBot | |
from ..constants import (GITHUB_WEBHOOK_HASH_HEADER, GITHUB_EVENT_HEADER, ALLOWED_EVENTS, GITHUB_APP_WEBHOOK_SECRET, | |
GITHUB_APP_PRIVATE_KEY, GITHUB_APP_ID) | |
logger = logging.getLogger() | |
class GitEventsViewSet(ViewSet): | |
def create(self, request): | |
""" This endpoint is where all Github events are posted. | |
This is where all the Github bots live. They are created and then the | |
request is passed to each bot in sequence. The bot will perform any | |
action that it needs to, based on the request. Afterwards the request | |
will be passed to the next bot. If the request does not pertain to a | |
bot it will do nothing. | |
Parameters | |
---------- | |
request : Request | |
The Github event POST request. | |
Returns : Response | |
The response to send back to Github based on the result of | |
processing the request. | |
""" | |
if not is_valid_github_webhook_hash(request.META.get(GITHUB_WEBHOOK_HASH_HEADER), request.body): | |
# Check that the request actually came from Github by making sure the encrypted webhook secret | |
# matches the secret we created. | |
logger.debug('Invalid webhook hash') | |
return Response({"message": "Invalid request hash. Only Github may call this endpoint."}, | |
status=HTTP_403_FORBIDDEN) | |
logger.debug('Valid webhook hash') | |
payload = JSONParser().parse(request) | |
event = request.META.get(GITHUB_EVENT_HEADER) | |
if event not in ALLOWED_EVENTS: | |
# Check that the event is one that we are expecting | |
logger.debug(f'Received a non-allowed event: {event}') | |
return Response({"message": f"This app is only designed to handle {ALLOWED_EVENTS} events"}, | |
status=HTTP_400_BAD_REQUEST) | |
try: | |
repo_client = RepositoryClient(private_key=GITHUB_APP_PRIVATE_KEY, app_id=GITHUB_APP_ID) | |
logger.debug('Authenticated with Github repo') | |
repo_installation_id = payload.get('installation', {}).get('id') | |
if not repo_client.is_valid_installation_id(repo_installation_id): | |
# Check that the GitHub event was generated by a valid installation of the Github App | |
logger.debug(f'Received event for repo: {repo_installation_id}') | |
return Response({"message": "This app only works with one repo"}, | |
status=HTTP_400_BAD_REQUEST) | |
performance_guard = PerformanceGuardBot(repo_client=repo_client, name='performance-guard') | |
performance_guard.run(event, payload) | |
# Add more PR bots here | |
except Exception as err: | |
logger.error(f'GitEventsViewSet create failed: {err}') | |
return Response({"message": err.message if hasattr(err, 'message') else str(err)}, | |
status=HTTP_500_INTERNAL_SERVER_ERROR) | |
return Response(status=HTTP_200_OK) | |
def is_valid_github_webhook_hash(hash_header, req_body): | |
""" Check that the hash passed with the request is valid based on the | |
webhook secret. | |
Parameters | |
---------- | |
hash_header : str | |
The Github hash header sent with the event. | |
req_body : dict | |
The body of the event sent by Github via a POST request. | |
Returns | |
------- | |
bool | |
If the hash is valid then this is True. False otherwise. | |
""" | |
alg, req_hash = hash_header.split('=', 1) | |
valid_hash = hmac.new(str.encode(GITHUB_APP_WEBHOOK_SECRET.strip()), req_body, alg) | |
return hmac.compare_digest(req_hash, valid_hash.hexdigest()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.urls import path, include | |
from rest_framework.routers import DefaultRouter | |
from .views.git_events_view import GitEventsViewSet | |
# This configures all the HTTP API routing and endpoints | |
router = DefaultRouter() | |
# Create a /git-events endpoint. | |
router.register(r'git-events', GitEventsViewSet, 'git-events') | |
urlpatterns = [ | |
path('', include(router.urls)), | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment