Skip to content

Commit

Permalink
add initial message endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
brngylni committed Jul 12, 2024
1 parent 64e63a8 commit b9b522a
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 1 deletion.
2 changes: 1 addition & 1 deletion webhook_to_fedora_messaging/config/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Defaults:
PERMANENT_SESSION_LIFETIME = 604800
SESSION_COOKIE_NAME = "user"


FASJSON_URL = "https://fasjson.tinystage.test/fasjson"
LOGGER_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
Expand Down
Empty file.
36 changes: 36 additions & 0 deletions webhook_to_fedora_messaging/endpoints/message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from flask import Blueprint, Flask, request, Response, Request
from ..database import db
from ..models.service import Service
from ..models.user import User
from sqlalchemy_helpers import get_or_create
from .util import not_found, success, bad_request, created, conflict, validate_request, create_event, query_fasjson
from .parser.parser import msg_parser
from fedora_messaging import api


app = Flask(__name__)
message_endpoint = Blueprint("message_endpoint", __name__)


@message_endpoint.route("/message", methods=["POST"])
def create_msg():
"""
Used for creating a new message by sending a post request to /message path
Request Body:
service_uuid: Service related to message.
"""
if not validate_request(request, fields=['service_uuid']):
return bad_request()

session = db.Session()
body = request.json

service = session.query(Service).filter(Service.id == body['service_uuid']).first()

if service is None:
return not_found()
msg = msg_parser(service.type, request)
api.publish(msg)

Empty file.
63 changes: 63 additions & 0 deletions webhook_to_fedora_messaging/endpoints/parser/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

from flask import Request
from werkzeug.datastructures.headers import EnvironHeaders
import hashlib
import hmac
from webhook_to_fedora_messaging_messages.github.github import GithubMessageV1
from ..util import fas_by_github



def msg_parser(service_type: str, request: Request):
if service_type.lower() == "github":
return _github_parser(request)


def _github_parser(request: Request, secret=True) -> GithubMessageV1:
"""
Convert Flask request objects into desired FedMsg format.
Args:
headers: Flask request.headers
body: A dictionary contains payload
secret: Specifies whether the webhook has secret key feature on or not
"""

headers = get_github_headers(request.headers)

secret = "00b863c62c96aa67aeea966104362b1136ce883d" # will come from a data source
if (secret and verify_signature(request.data, secret, headers['X-Hub-Signature-256'])) or not secret:
topic = "github." + headers['X-Github-Event']
agent = fas_by_github(request.json['sender']['login']) # FASJSON
return GithubMessageV1(topic=topic, body={'body': request.json, 'headers': headers, 'agent': agent})
return None


def get_github_headers(headers: EnvironHeaders) -> dict:
"""To change from EnvironHeaders to dict since EnvironHeaders doesn't have a copy() method.
Args:
headers: Flask request.headers
"""

return dict((key, headers[key]) for key in headers.keys())


def verify_signature(payload_body: str, secret_token: str, signature_header: str) -> bool:
"""Verify that the payload was sent from GitHub by validating SHA256.
Return false if not authorized.
Args:
payload_body: original request body to verify (request.body())
secret_token: GitHub app webhook token (WEBHOOK_SECRET)
signature_header: header received from GitHub (x-hub-signature-256)
"""
if not signature_header:
return False
hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)
expected_signature = "sha256=" + hash_object.hexdigest()

if not hmac.compare_digest(expected_signature, signature_header):
return False
return True
60 changes: 60 additions & 0 deletions webhook_to_fedora_messaging/endpoints/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from flask import Response, Request
from ..config.defaults import FASJSON_URL
import fasjson_client


def not_found() -> Response:
return Response({'message': 'Not Found'}, status=404, mimetype='application/json')


def success(data: dict) ->Response:
return Response(data, status=200, mimetype='application/json')


def bad_request() -> Response:
return Response("{'message': 'Bad Request'}", status=400, mimetype='application/json')


def created(data: dict) -> Response:
return Response(data, status=201, mimetype='application/json')


def conflict(data: dict) -> Response:
return Response(data, status=409, mimetype='application/json')


def validate_request(request: Request, fields=['username']):
return all(field in request for field in fields)


def verify_signature(payload_body: str, secret_token: str, signature_header: str) -> bool:
"""Verify that the payload was sent from GitHub by validating SHA256.
Raise and return 403 if not authorized.
Args:
payload_body: original request body to verify (request.body())
secret_token: GitHub app webhook token (WEBHOOK_SECRET)
signature_header: header received from GitHub (x-hub-signature-256)
"""
if not signature_header:
return False
hash_object = hmac.new(secret_token.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)
expected_signature = "sha256=" + hash_object.hexdigest()

if not hmac.compare_digest(expected_signature, signature_header):
return False
return True


def fas_by_github(username: str) -> str:
"""Get the Fedora Account System Username of the given github username
Args:
username: Github Username"""

fasjson = fasjson_client.Client(FASJSON_URL)
response = fasjson.search(github_username=username)
if response.result and len(response.result) == 1:
return response.result[0]["username"]
return None

0 comments on commit b9b522a

Please sign in to comment.