From b25b533783008de1a89e6fb2a84b8d4225246d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Piboub=C3=A8s?= Date: Thu, 20 Aug 2015 00:49:25 +0200 Subject: [PATCH] Bundling webserver into RTV --- MANIFEST.in | 1 + rtv/__main__.py | 4 +-- rtv/config.py | 2 +- rtv/oauth.py | 67 ++++++++++++++++++++++++++++++++++++++++--------- setup.py | 2 +- 5 files changed, 59 insertions(+), 17 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index fdc4d5e..02ad4bd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include version.py include CHANGELOG.rst CONTRIBUTORS.rst LICENSE +include rtv/templates/*.html diff --git a/rtv/__main__.py b/rtv/__main__.py index 021872e..bb9337d 100644 --- a/rtv/__main__.py +++ b/rtv/__main__.py @@ -152,7 +152,7 @@ def main(): reddit.config.decode_html_entities = False with curses_session() as stdscr: oauth = OAuthTool(reddit, stdscr, LoadScreen(stdscr)) - if args.auto_login == 'True': + if args.auto_login == 'True': # Ew! oauth.authorize() if args.link: page = SubmissionPage(stdscr, reddit, oauth, url=args.link) @@ -160,8 +160,6 @@ def main(): subreddit = args.subreddit or 'front' page = SubredditPage(stdscr, reddit, oauth, subreddit) page.loop() - except praw.errors.InvalidUserPass: - print('Invalid password for username: {}'.format(args.username)) except praw.errors.OAuthAppRequired: print('Invalid OAuth app config parameters') except praw.errors.OAuthInvalidToken: diff --git a/rtv/config.py b/rtv/config.py index e7761af..53f4c59 100644 --- a/rtv/config.py +++ b/rtv/config.py @@ -10,5 +10,5 @@ OAuth settings oauth_client_id = 'nxoobnwO7mCP5A' oauth_client_secret = 'praw_gapfill' -oauth_redirect_uri = 'https://rtv.theo-piboubes.fr/auth' +oauth_redirect_uri = 'http://127.0.0.1:65000/auth' oauth_scope = 'edit-history-identity-mysubreddits-privatemessages-read-report-save-submit-subscribe-vote' diff --git a/rtv/oauth.py b/rtv/oauth.py index c5482b2..3377fc2 100644 --- a/rtv/oauth.py +++ b/rtv/oauth.py @@ -1,4 +1,4 @@ -import configparser +from six.moves import configparser import curses import logging import os @@ -11,11 +11,37 @@ import praw from . import config from .curses_helpers import show_notification, prompt_input +from tornado import ioloop, web + __all__ = ['token_validity', 'OAuthTool'] _logger = logging.getLogger(__name__) token_validity = 3540 +oauth_state = None +oauth_code = None +oauth_error = None + +class HomeHandler(web.RequestHandler): + + def get(self): + self.render('home.html') + +class AuthHandler(web.RequestHandler): + + def get(self): + global oauth_state + global oauth_code + global oauth_error + + oauth_state = self.get_argument('state', default='state_placeholder') + oauth_code = self.get_argument('code', default='code_placeholder') + oauth_error = self.get_argument('error', default='error_placeholder') + + self.render('auth.html', state=oauth_state, code=oauth_code, error=oauth_error) + + ioloop.IOLoop.current().stop() + class OAuthTool(object): def __init__(self, reddit, stdscr=None, loader=None, @@ -38,6 +64,13 @@ class OAuthTool(object): self.token_expiration = 0 + # Initialize Tornado webapp and listen on port 65000 + self.callback_app = web.Application([ + (r'/', HomeHandler), + (r'/auth', AuthHandler), + ], template_path='rtv/templates') + self.callback_app.listen(65000) + def get_config_fp(self): HOME = os.path.expanduser('~') XDG_CONFIG_HOME = os.getenv('XDG_CONFIG_HOME', @@ -105,27 +138,37 @@ class OAuthTool(object): permission_ask_page_link = self.reddit.get_authorize_url(str(hex_uuid), scope=self.scope, refreshable=True) - webbrowser.open(permission_ask_page_link) - show_notification(self.stdscr, ['Access prompt opened in web browser']) + with self.loader(message='Waiting for authorization'): + webbrowser.open(permission_ask_page_link) - final_state = prompt_input(self.stdscr, 'State: ') - final_code = prompt_input(self.stdscr, 'Code: ') + ioloop.IOLoop.current().start() - if not final_state or not final_code: - curses.flash() + global oauth_state + global oauth_code + global oauth_error + + self.final_state = oauth_state + self.final_code = oauth_code + self.final_error = oauth_error + + # Check if access was denied + if self.final_error == 'access_denied': + show_notification(self.stdscr, ['Declined access']) + return + elif self.final_error != 'error_placeholder': + show_notification(self.stdscr, ['Authentication error']) return - # Check if UUID matches obtained state # (if not, authorization process is compromised, and I'm giving up) - if hex_uuid != final_state: + elif hex_uuid != self.final_state: show_notification(self.stdscr, ['UUID mismatch, stopping.']) return - # Get access information (tokens and scopes) - self.access_info = self.reddit.get_access_information(final_code) - try: with self.loader(message='Logging in'): + # Get access information (tokens and scopes) + self.access_info = self.reddit.get_access_information(self.final_code) + self.reddit.set_access_credentials( scope=set(self.access_info['scope']), access_token=self.access_info['access_token'], diff --git a/setup.py b/setup.py index 48e5e1b..38d0d08 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( keywords='reddit terminal praw curses', packages=['rtv'], include_package_data=True, - install_requires=['praw>=3.1.0', 'six', 'requests', 'kitchen'], + install_requires=['tornado', 'praw>=3.1.0', 'six', 'requests', 'kitchen'], entry_points={'console_scripts': ['rtv=rtv.__main__:main']}, classifiers=[ 'Intended Audience :: End Users/Desktop',