fix security issue and run black

This commit is contained in:
Micha Albert 2024-08-31 00:05:35 +00:00
parent 3d08445e0f
commit 75882ddccb

View file

@ -3,10 +3,12 @@ import hmac
import json import json
import os import os
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from datetime import datetime
from random import choice from random import choice
from secrets import token_hex from secrets import token_hex
from typing import Dict, List from typing import Dict, List
import cv2
import httpx import httpx
import uvicorn import uvicorn
from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.schedulers.asyncio import AsyncIOScheduler
@ -19,6 +21,7 @@ from fastapi.responses import HTMLResponse, RedirectResponse
from prisma import Prisma from prisma import Prisma
from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
from slack_bolt.async_app import AsyncAck, AsyncApp from slack_bolt.async_app import AsyncAck, AsyncApp
from yarl import URL
load_dotenv(dotenv_path="./.env") load_dotenv(dotenv_path="./.env")
@ -30,6 +33,15 @@ scheduler = AsyncIOScheduler()
FERNET = Fernet(os.environ["FERNET_KEY"]) FERNET = Fernet(os.environ["FERNET_KEY"])
def get_recording_duration(timestamp, stream_key):
vid = cv2.VideoCapture(
f"/home/onboard/recordings/{stream_key}/{datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%fZ').strftime('%Y-%m-%d_%H-%M-%S-%f')}.mp4"
)
return int(
(vid.get(cv2.CAP_PROP_FRAME_COUNT) / vid.get(cv2.CAP_PROP_FPS)) / 60
) # seconds to minutes
def verify_gh_signature(payload_body, secret_token, signature_header): def verify_gh_signature(payload_body, secret_token, signature_header):
"""Verify that the payload was sent from GitHub by validating SHA256. """Verify that the payload was sent from GitHub by validating SHA256.
@ -54,7 +66,14 @@ def verify_gh_signature(payload_body, secret_token, signature_header):
async def get_recording_list(stream_key: str) -> List[str]: async def get_recording_list(stream_key: str) -> List[str]:
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
return [recording["start"] for recording in (await client.get(f"http://localhost:9997/v3/recordings/get/{stream_key}")).json()["segments"]] return [
recording["start"]
for recording in (
await client.get(
f"http://localhost:9997/v3/recordings/get/{stream_key}"
)
).json()["segments"]
]
async def update_active(): async def update_active():
@ -167,7 +186,19 @@ bolt_handler = AsyncSlackRequestHandler(bolt)
@api.get("/auth/github/login") @api.get("/auth/github/login")
async def github_redirect(request: Request): async def github_redirect(request: Request):
return RedirectResponse( return RedirectResponse(
f"https://github.com/login/oauth/authorize?client_id={os.environ['GH_CLIENT_ID']}&redirect_uri=https://live.onboard.hackclub.com/auth/github/callback&scopes=read:user&state={request.query_params['state']}" str(
URL.build(
scheme="https",
host="github.com",
path="/login/oauth/authorize",
query={
"client_id": os.environ["GH_CLIENT_ID"],
"redirect_uri": "https://live.onboard.hackclub.com/auth/github/callback",
"scopes": "read:user",
"state": request.query_params["state"],
},
)
)
) )
@ -177,7 +208,9 @@ async def github_callback(request: Request):
state: str = request.query_params["state"] state: str = request.query_params["state"]
user_id, pr_id = FERNET.decrypt(bytes.fromhex(state)).decode().split("+") user_id, pr_id = FERNET.decrypt(bytes.fromhex(state)).decode().split("+")
db_user = await db.user.find_first_or_raise(where={"slack_id": user_id}) db_user = await db.user.find_first_or_raise(where={"slack_id": user_id})
user_stream_key = (await db.stream.find_first_or_raise(where={"user_id": db_user.id})).key user_stream_key = (
await db.stream.find_first_or_raise(where={"user_id": db_user.id})
).key
db_pr = await db.pullrequest.find_first_or_raise(where={"github_id": int(pr_id)}) db_pr = await db.pullrequest.find_first_or_raise(where={"github_id": int(pr_id)})
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
token = ( token = (
@ -228,23 +261,57 @@ async def github_callback(request: Request):
"type": "section", "type": "section",
"text": { "text": {
"type": "mrkdwn", "type": "mrkdwn",
"text": "This is a section block with checkboxes.", "text": f"Here are all your sessions. Select the ones associated with OnBoard pull request #{pr_id}:",
}, },
"accessory": { "accessory": {
"type": "checkboxes", "type": "checkboxes",
"options": [ "options": [
json.loads("""{{"text": {{ "type": "mrkdwn", "text": "Your session on {pretty_time}"}}, "description": {{"type": "mrkdwn", "text": "You streamed for {length}"}}, "value": "checkbox-{filename}"}}""".format(pretty_time=recording, length=1, filename=recording)) for recording in stream_recs json.loads(
"""{{"text": {{ "type": "mrkdwn", "text": "Your session on {pretty_time}"}}, "description": {{"type": "mrkdwn", "text": "You streamed for {length} {minute_or_minutes}"}}, "value": "checkbox-{filename}"}}""".format(
pretty_time=recording,
length=get_recording_duration(
recording, user_stream_key
),
minute_or_minutes=(
"minute"
if get_recording_duration(
recording, user_stream_key
)
== 1
else "minutes"
),
filename=recording,
)
)
for recording in stream_recs
], ],
"action_id": "checkboxes", "action_id": "checkboxes",
}, },
}, },
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": True,
"text": "Submit",
},
"style": "primary",
"value": "submit_sessions",
"action_id": "submit_sessions",
},
],
},
], ],
) )
return HTMLResponse( return HTMLResponse(
"<h1>Success! Your PR has been linked to your account. Check your Slack DMs for the next steps!</h1>" "<h1>Success! Your PR has been linked to your Slack account. Check your Slack DMs for the next steps!</h1>"
) )
return HTMLResponse( return HTMLResponse(
f"<h1>Looks like something went wrong! DM @mra on slack.</h1><p>This info might be of use to them: {FERNET.encrypt(bytes(str(db_pr.gh_user_id) + " " + str(gh_user) + " " + user_id + " " + pr_id + " " + state, encoding='utf-8'))}</p>", status_code=403 f"<h1>Looks like something went wrong! DM @mra on slack.</h1><p>This info might be of use to them: {FERNET.encrypt(bytes(str(db_pr.gh_user_id) + " " + str(gh_user) + " " + user_id + " " + pr_id + " " + state, encoding='utf-8'))}</p>",
status_code=403,
) )