-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add initial message endpoint #31
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from flask import Blueprint, abort | ||
from ..database import db | ||
from ..models.service import Service | ||
from .parser.parser import msg_parser | ||
from fedora_messaging import api | ||
from webhook_to_fedora_messaging.exceptions import SignatureMatchError | ||
from sqlalchemy import select | ||
from sqlalchemy.exc import NoResultFound | ||
|
||
|
||
message_endpoint = Blueprint("message_endpoint", __name__) | ||
|
||
|
||
@message_endpoint.route("/<service_uuid>", methods=["POST"]) | ||
def create_msg(service_uuid): | ||
""" | ||
Used for creating a new message by sending a post request to /message path | ||
|
||
Request Body: | ||
service_uuid: Service related to message. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't expect the message body to always contain a key named |
||
|
||
""" | ||
|
||
try: | ||
service = db.session.execute(select(Service).where(Service.uuid == service_uuid)).scalar_one() | ||
except NoResultFound: | ||
return {'message': 'Service UUID Not Found'}, 404 | ||
|
||
try: | ||
msg = msg_parser(service.type, service.token) | ||
except SignatureMatchError as e: | ||
return abort(400, {'message': str(e)}) | ||
except ValueError as e: | ||
return abort(400, {'message': str(e)}) | ||
|
||
api.publish(msg) | ||
return {'status': 'OK', 'message_id': msg.id} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from flask import request, current_app | ||
import hashlib | ||
import hmac | ||
from webhook_to_fedora_messaging_messages.github.github import GithubMessageV1 | ||
from webhook_to_fedora_messaging.exceptions import SignatureMatchError | ||
import fasjson_client | ||
|
||
|
||
def github_parser(secret: str) -> GithubMessageV1: | ||
"""Convert Flask request objects into desired FedMsg format. | ||
|
||
Args: | ||
secret: Specifies whether the webhook has secret key feature on or not | ||
""" | ||
|
||
headers = dict(request.headers) | ||
|
||
if secret and not verify_signature(secret, headers['X-Hub-Signature-256']): | ||
raise SignatureMatchError("Message Signature Couldn't be Matched.") | ||
|
||
topic = f"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}) | ||
|
||
|
||
def verify_signature(secret_token: str, signature_header: str) -> bool: | ||
"""Verify that the payload was sent from GitHub by validating SHA256. | ||
|
||
Return false if not authorized. | ||
|
||
Args: | ||
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=request.data, digestmod=hashlib.sha256) | ||
expected_signature = "sha256=" + hash_object.hexdigest() | ||
|
||
return hmac.compare_digest(expected_signature, signature_header) | ||
|
||
|
||
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(current_app.config["FASJSON_URL"]) | ||
response = fasjson.search(github_username=username) | ||
if response.result and len(response.result) == 1: | ||
return response.result[0]["username"] | ||
return None |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from .github import github_parser | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove the empty line from the top of the file. |
||
def msg_parser(service_type: str, secret: str): | ||
if service_type.lower() == "github": | ||
return github_parser(secret) | ||
else: | ||
raise ValueError(f"Unsupported Service: {service_type}") |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,7 @@ def __init__(self, text): | |
|
||
def __str__(self): | ||
return self.text | ||
|
||
class SignatureMatchError(Exception): | ||
pass | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blueprints are supposed to contain more than one view. You can define a generic blueprint for the app in
endpoints.__init__.py
and register the views with it.Examples: https://github.com/fedora-infra/noggin/blob/dev/noggin/controller/__init__.py and https://github.com/fedora-infra/noggin/blob/dev/noggin/controller/root.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may conflict with other branches I guess. We might need another pr to switch this to the one you supposed. Since the other branches are merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand. Which branches will it conflict with?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the code you suggested on local but after a meeting with @gridhead we thought the current one might be better. So whichever you guys decide I can push.