Skip to content
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 a GitHub App support for aiohttp implementation #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion browntruck/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from aiocron import crontab
from aiohttp import web

from browntruck.integration_installation import integration_installation_hook
from browntruck.news import news_hook
from browntruck.rebase import check_prs, needs_rebase_hook

Expand All @@ -37,18 +38,30 @@ async def _shutdown_redis_pool(app):
await app["redis.pool"].wait_closed()


def create_app(*, github_token, github_payload_key, repo, redis_url,
def create_app(*, github_token, github_payload_key,
github_app_id,
github_app_private_key,
# github_app_webhook_secret, # seems == github_payload_key
repo, redis_url,
loop=None):
app = web.Application(loop=loop)
app["repo"] = repo
app["github_token"] = github_token
app["github_payload_key"] = github_payload_key
app["github_app_id"] = github_app_id
app["github_app_private_key"] = github_app_private_key
# app["github_app_webhook_secret"] = github_app_webhook_secret
app["redis.url"] = redis_url

app.on_startup.append(_create_redis_pool)

app.on_cleanup.append(_shutdown_redis_pool)

app.router.add_post(
"/hooks/integration_installation",
integration_installation_hook,
)

app.router.add_post("/hooks/news", news_hook)
app.router.add_post("/hooks/rebase", needs_rebase_hook)

Expand All @@ -64,6 +77,11 @@ def main(argv):
app = create_app(
github_token=os.environ.get("GITHUB_TOKEN"),
github_payload_key=os.environ.get("GITHUB_PAYLOAD_KEY"),
# GitHub App integration credentials:
github_app_id=os.environ.get("GITHUB_APP_IDENTIFIER"),
github_app_private_key=os.environ.get("GITHUB_PRIVATE_KEY"),
# github_app_webhook_secret=os.environ.get("GITHUB_WEBHOOK_SECRET"), # seems to be the same as github_payload_key
# GitHub App integration credentials end
repo=os.environ.get("REPO"),
redis_url=os.environ.get("REDIS_URL"),
loop=loop,
Expand Down
45 changes: 45 additions & 0 deletions browntruck/integration_installation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json

import aiohttp
import gidgethub.aiohttp

from aiohttp import web

from browntruck.utils import get_install_token


async def integration_installation_hook(request):
payload = await request.read()

data = json.loads(payload.decode(request.charset or "utf8"))
installation_token = await get_install_token(
app_id=app["github_app_id"],
private_key=app["github_app_private_key"],
install_id=data["installation"]["id"],
payload=data,
)

async with aiohttp.ClientSession() as session:
gh = gidgethub.aiohttp.GitHubAPI(
session,
"BrownTruck-Bot" # TODO: add "/1.0" as in version
" (+https://github.com/pypa/browntruck)",
oauth_token=request.app["github_token"],
)
# ...

return web.json_response({
"message": "installation recorded",
})
35 changes: 35 additions & 0 deletions browntruck/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
# limitations under the License.

import hmac
import time

import aiohttp
import gidgethub.aiohttp
import jwt

class InvalidSignature(Exception):
pass
Expand All @@ -22,3 +26,34 @@ def verify_signature(key, signature, body):
if not hmac.compare_digest(f"sha1={digest.hexdigest().lower()}",
signature.lower()):
raise InvalidSignature


def get_gh_jwt(app_id, private_key):
"""Create a signed JWT, valid for 60 seconds."""
now = int(time.time())
payload = {
"iat": now,
"exp": now + 60,
"iss": app_id
}
return jwt.encode(
payload,
key=private_key,
algorithm="RS256"
).decode('utf-8')


async def get_install_token(*, app_id, private_key, install_id, payload):
gh_jwt = get_gh_jwt(app_id, private_key)
async with aiohttp.ClientSession() as session:
gh = gidgethub.aiohttp.GitHubAPI(
session,
"BrownTruck-Bot" # TODO: add "/1.0" as in version
" (+https://github.com/pypa/browntruck)",
jwt=gh_jwt,
)
return (
await gh.getitem(
payload["installation"]["access_tokens_url"]
)["token"]
)
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ gidgethub==2.0.0
hiredis==0.2.0
multidict==2.1.4
pycares==2.1.1
pyjwt
python-dateutil==2.6.0
six==1.10.0
unidiff==0.5.2+new.files
Expand Down