From 0533aac9b3124663cb56ee6f4f1593c1e0d10fc2 Mon Sep 17 00:00:00 2001 From: harmacist Date: Fri, 30 Sep 2022 21:27:25 -0500 Subject: [PATCH] Initial Commit - Flask --- .gitignore | 2 + .idea/.gitignore | 8 ++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 4 + .idea/modules.xml | 8 ++ .idea/sinkhole.iml | 10 ++ .idea/vcs.xml | 6 + src/app.py | 111 ++++++++++++++++++ 8 files changed, 155 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/sinkhole.iml create mode 100644 .idea/vcs.xml create mode 100644 src/app.py diff --git a/.gitignore b/.gitignore index 55be276..0e56eec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +refs/ + # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5431ca1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..f1198c9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/sinkhole.iml b/.idea/sinkhole.iml new file mode 100644 index 0000000..35a5e63 --- /dev/null +++ b/.idea/sinkhole.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..fa5d4cd --- /dev/null +++ b/src/app.py @@ -0,0 +1,111 @@ +from flask import Flask, request, redirect, abort, url_for +from urllib.parse import quote_plus +from base64 import b64encode +import datetime +import requests +import uuid +import os + + +app = Flask(__name__) + +STATE = quote_plus(str(uuid.uuid4())) +TOKEN = None +AGENT = f"sinkhole v0.0.1" +CLIENT_ID = os.getenv('REDDIT_APP_ID', '') +CLIENT_SECRET = os.getenv('CLIENT_SECRET', '') +REDIRECT_URI = quote_plus(os.getenv('REDIRECT_URI', '')) +DURATION = quote_plus(os.getenv('DURATION', 'temporary')) +SCOPE = ','.join([ + 'identity', + 'edit', + 'flair', + 'history', + 'modconfig', + 'modflair', + 'modlog', + 'modposts', + 'modwiki', + 'mysubreddits', + 'privatemessages', + 'read', + 'report', + 'save', + 'submit', + 'subscribe', + 'vote', + 'wikiedit', + 'wikiread' +]) + + +def get_bearer_token( + code: str +) -> dict: + token_resp = requests.post( + "https://www.reddit.com/api/v1/access_token", + headers={ + 'Accept': 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': AGENT, + 'Authorization': f"Basic {b64encode(f'{CLIENT_ID}:{CLIENT_SECRET}'.encode()).decode('utf-8')}" + }, + data=f"grant_type=authorization_code&code={code}&redirect_uri={REDIRECT_URI}" + ) + + token_data = token_resp.json() + expiration_ts = (datetime.datetime.now() + datetime.timedelta(seconds=token_data['expires_in'])).isoformat() + return { + 'access_token': token_data['access_token'], + 'token_type': token_data['token_type'], + 'expiration_ts': expiration_ts, + 'scope': token_data['scope'] + } + + +@app.route("/") +def index(): + if TOKEN: + return "You are authorized, way to go!" + auth_url = f"https://www.reddit.com/api/v1/authorize.compact?client_id={quote_plus(CLIENT_ID)}" \ + f"&response_type=code" \ + f"&state={STATE}" \ + f"&redirect_uri={REDIRECT_URI}" \ + f"&duration={DURATION}" \ + f"&scope={SCOPE}" + return f"Please navigate here to authorize." + + +@app.route('/home', methods=['POST']) +def home(): + token = request.form.get('token') + return { + 'TOKEN': request.form.get('token'), + 'TOKEN_TYPE': request.form.get('token_type'), + 'TOKEN_EXPIRATION': request.form.get('token_expire_dt'), + 'TOKEN_SCOPE': request.form.get('token_scope') + } + + +@app.route("/callback", methods=['GET', 'POST']) +def auth_callback(**kwargs): + error = request.args.get('error') + state = request.args.get('state') + code = request.args.get('code') + + if not error: + if state == STATE: + # get the bearer token and redirect + token_info = get_bearer_token(code) + return redirect(url_for('home', + token=token_info.get('access_token'), + token_type=token_info.get('token_type'), + token_expire_dt=token_info.get('expiration_ts'), + token_scope=token_info.get('scope') + ), + code=307) + else: + return f"Not authorized! State {state} didn't match {STATE} that was sent!" + else: + abort(403) +