Skip to content

Instantly share code, notes, and snippets.

@bialesdaniel
Last active February 11, 2021 22:40
Show Gist options
  • Save bialesdaniel/12f2ce1a5a5802ae5e099a7476d81087 to your computer and use it in GitHub Desktop.
Save bialesdaniel/12f2ce1a5a5802ae5e099a7476d81087 to your computer and use it in GitHub Desktop.
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())
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