added secret verification to webhook endpoint w/ burnettk

This commit is contained in:
jasquat 2022-12-09 16:51:00 -05:00
parent d7221690f0
commit 6122fb0ae5
7 changed files with 63 additions and 8 deletions

View File

@ -61,8 +61,9 @@ SPIFFWORKFLOW_BACKEND_LOG_LEVEL = environ.get(
# When a user clicks on the `Publish` button, this is the default branch this server merges into.
# I.e., dev server could have `staging` here. Staging server might have `production` here.
GIT_BRANCH_TO_PUBLISH_TO = environ.get("GIT_BRANCH_TO_PUBLISH_TO", default="staging")
GIT_CLONE_URL_FOR_PUBLISHING = environ.get("GIT_CLONE_URL", default=None)
GIT_BRANCH_TO_PUBLISH_TO = environ.get("GIT_BRANCH_TO_PUBLISH_TO")
GIT_BRANCH = environ.get("GIT_BRANCH")
GIT_CLONE_URL_FOR_PUBLISHING = environ.get("GIT_CLONE_URL")
GIT_COMMIT_ON_SAVE = environ.get("GIT_COMMIT_ON_SAVE", default="false") == "true"
# Datbase Configuration

View File

@ -17,3 +17,5 @@ GIT_CLONE_URL_FOR_PUBLISHING = environ.get(
)
GIT_USERNAME = "sartography-automated-committer"
GIT_USER_EMAIL = f"{GIT_USERNAME}@users.noreply.github.com"
GIT_BRANCH_TO_PUBLISH_TO = "main"
GIT_BRANCH = "main"

View File

@ -0,0 +1,4 @@
"""staging."""
GIT_BRANCH = "staging"
GIT_BRANCH_TO_PUBLISH_TO = "main"
GIT_COMMIT_ON_SAVE = False

View File

@ -1826,8 +1826,12 @@ def get_spiff_task_from_process_instance(
# where 7000 is the port the app is running on locally
def github_webhook_receive(body: Dict) -> Response:
"""Github_webhook_receive."""
GitService.handle_web_hook(body)
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
auth_header = request.headers.get("X-Hub-Signature-256")
AuthorizationService.verify_sha256_token(auth_header)
result = GitService.handle_web_hook(body)
return Response(
json.dumps({"git_pull": result}), status=200, mimetype="application/json"
)
#

View File

@ -1,6 +1,9 @@
"""Authorization_service."""
import inspect
import re
from hashlib import sha256
from hmac import compare_digest
from hmac import HMAC
from typing import Optional
from typing import Union
@ -45,6 +48,27 @@ class UserDoesNotHaveAccessToTaskError(Exception):
class AuthorizationService:
"""Determine whether a user has permission to perform their request."""
# https://stackoverflow.com/a/71320673/6090676
@classmethod
def verify_sha256_token(cls, auth_header: Optional[str]) -> None:
"""Verify_sha256_token."""
if auth_header is None:
raise ApiError(
error_code="unauthorized",
message="",
status_code=403,
)
received_sign = auth_header.split("sha256=")[-1].strip()
secret = current_app.config["GITHUB_WEBHOOK_SECRET"].encode()
expected_sign = HMAC(key=secret, msg=request.data, digestmod=sha256).hexdigest()
if not compare_digest(received_sign, expected_sign):
raise ApiError(
error_code="unauthorized",
message="",
status_code=403,
)
@classmethod
def has_permission(
cls, principals: list[PrincipalModel], permission: str, target_uri: str

View File

@ -140,7 +140,7 @@ class GitService:
# only supports github right now
@classmethod
def handle_web_hook(cls, webhook: dict) -> None:
def handle_web_hook(cls, webhook: dict) -> bool:
"""Handle_web_hook."""
cls.check_for_configs()
@ -155,8 +155,25 @@ class GitService:
f"Configured clone url does not match clone url from webhook: {clone_url}"
)
if "ref" not in webhook:
raise InvalidGitWebhookBodyError(
f"Could not find the 'ref' arg in the webhook boy: {webhook}"
)
if current_app.config["GIT_BRANCH"] is None:
raise MissingGitConfigsError(
"Missing config for GIT_BRANCH. "
"This is required for updating the repository as a result of the webhook"
)
ref = webhook["ref"]
git_branch = current_app.config["GIT_BRANCH"]
if ref != f"refs/heads/{git_branch}":
return False
with FileSystemService.cd(current_app.config["BPMN_SPEC_ABSOLUTE_DIR"]):
cls.run_shell_command(["git", "pull"])
return True
@classmethod
def publish(cls, process_model_id: str, branch_to_update: str) -> str:

View File

@ -9,7 +9,6 @@ import pytest
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from spiffworkflow_backend.services.git_service import GitService
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
@ -33,6 +32,7 @@ from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.services.authorization_service import AuthorizationService
from spiffworkflow_backend.services.file_system_service import FileSystemService
from spiffworkflow_backend.services.git_service import GitService
from spiffworkflow_backend.services.process_instance_processor import (
ProcessInstanceProcessor,
)
@ -2573,7 +2573,8 @@ class TestProcessApi(BaseTest):
assert "On branch main" in output
assert "No commits yet" in output
assert (
'nothing to commit (create/copy files and use "git add" to track)' in output
'nothing to commit (create/copy files and use "git add" to track)'
in output
)
process_group_id = "test_group"
@ -2593,7 +2594,9 @@ class TestProcessApi(BaseTest):
bpmn_file_name=bpmn_file_name,
bpmn_file_location=bpmn_file_location,
)
process_model_absolute_dir = os.path.join(bpmn_root, process_model_identifier)
process_model_absolute_dir = os.path.join(
bpmn_root, process_model_identifier
)
output = GitService.run_shell_command_to_get_stdout(["git", "status"])
test_string = 'Untracked files:\n (use "git add <file>..." to include in what will be committed)\n\ttest_group'