From 979726ec6b7777d5da9f0afc5bd15b3211e1a125 Mon Sep 17 00:00:00 2001 From: Michael Lazar Date: Thu, 8 Jun 2017 01:04:29 -0400 Subject: [PATCH] Automatically delete refresh-token if it is corrupted --- rtv/exceptions.py | 6 ++- rtv/oauth.py | 25 +++++++-- .../test_oauth_authorize_invalid_token.yaml | 51 +++++++++++++++++++ tests/test_oauth.py | 10 ++++ 4 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 tests/cassettes/test_oauth_authorize_invalid_token.yaml diff --git a/rtv/exceptions.py b/rtv/exceptions.py index 4cd582b..b6fdca9 100644 --- a/rtv/exceptions.py +++ b/rtv/exceptions.py @@ -48,4 +48,8 @@ class TemporaryFileError(RTVError): class MailcapEntryNotFound(RTVError): - "A valid mailcap entry could not be coerced from the given url" \ No newline at end of file + "A valid mailcap entry could not be coerced from the given url" + + +class InvalidRefreshToken(RTVError): + "The refresh token is corrupt and cannot be used to login" diff --git a/rtv/oauth.py b/rtv/oauth.py index b78e7f7..2b8a263 100644 --- a/rtv/oauth.py +++ b/rtv/oauth.py @@ -15,6 +15,8 @@ from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from . import docs from .config import TEMPLATES +from .exceptions import InvalidRefreshToken +from .packages.praw.errors import HTTPException, OAuthException _logger = logging.getLogger(__name__) @@ -131,8 +133,25 @@ class OAuthHelper(object): # If we already have a token, request new access credentials if self.config.refresh_token: with self.term.loader('Logging in'): - self.reddit.refresh_access_information( - self.config.refresh_token) + try: + self.reddit.refresh_access_information( + self.config.refresh_token) + except (HTTPException, OAuthException) as e: + # Reddit didn't accept the refresh-token + # This appears to throw a generic 400 error instead of the + # more specific invalid_token message that it used to send + if isinstance(e, HTTPException): + if e._raw.status_code != 400: + # No special handling if the error is something + # temporary like a 5XX. + raise e + + # Otherwise we know the token is bad, so we can remove it. + _logger.exception(e) + self.clear_oauth_data() + raise InvalidRefreshToken( + ' Invalid user credentials!\n' + 'The cached refresh token has been removed') return state = uuid.uuid4().hex @@ -207,4 +226,4 @@ class OAuthHelper(object): def clear_oauth_data(self): self.reddit.clear_authentication() - self.config.delete_refresh_token() \ No newline at end of file + self.config.delete_refresh_token() diff --git a/tests/cassettes/test_oauth_authorize_invalid_token.yaml b/tests/cassettes/test_oauth_authorize_invalid_token.yaml new file mode 100644 index 0000000..3cb893d --- /dev/null +++ b/tests/cassettes/test_oauth_authorize_invalid_token.yaml @@ -0,0 +1,51 @@ +interactions: +- request: + body: grant_type=refresh_token&redirect_uri=http%3A%2F%2F127.0.0.1%3A65000%2F&refresh_token=********** + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: ['**********'] + Connection: [keep-alive] + Content-Length: ['99'] + Content-Type: [application/x-www-form-urlencoded] + User-Agent: [rtv test suite PRAW/3.6.1 Python/3.6.1 b'Darwin-14.5.0-x86_64-i386-64bit'] + method: POST + uri: https://api.reddit.com/api/v1/access_token/ + response: + body: {string: '{"message": "Bad Request", "error": 400}'} + headers: + Accept-Ranges: [bytes] + Connection: [keep-alive] + Content-Length: ['40'] + Content-Type: [application/json; charset=UTF-8] + Date: ['Thu, 08 Jun 2017 04:57:59 GMT'] + Server: [snooserv] + Set-Cookie: ['session_tracker=2Pb665rWf1oCGZfV3L.0.1496897879415.Z0FBQUFBQlpPTmxYQl9PVkQ5ekc5U3FDdWZ3cTZMRlRMWVMtZmdhd1duVGdLYTFva09aZmVIazNucGdHSV9tOV9qUy1FZ0M0RS15aWNYVDNxSWpzU1lOTnBQcGpoU19fY0JuM3NnczVyZ3JBcGhKYUQ0RzVPWXUyb0t4dkt3Y1kzRkRvWHg0TXFJbjI; + Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 08-Jun-2017 06:57:59 + GMT; secure', 'loid=000000000003fg1hbe.2.1496897879427.Z0FBQUFBQlpPTmxYX2xyMng2Mlk4bmVsUmt6bU04M01ZdTZmZGZ6a3JINnlqdnJKWjZINHNvUFNKc0FJUGVkU2NTem1ycXk3LW1WdXp1bERTM0RsU1J6MlFZZVNsRGZoOGFoREY2QU16ckpHQm01dHI0N2lES2JocmM2ZXVnVXphdFUzUi1yQ3VsQTQ; + Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sat, 08-Jun-2019 04:57:59 + GMT; secure', 'session_tracker=vxk8Omjl7TIIb6u89z.0.1496897879423.Z0FBQUFBQlpPTmxYSmFBVDFlRG42OWZCN1V2R2QyVWlhd3VoaWxHWXBUZE9KRGpLSHZOWmhVVHVrTEdIaWQyWW04cXpnMG1TZ2JRUWRMMU5zc1JQQ21ta2NGcGx5RzZtZjNocmlrVEJzTmVuMnBER04ycWN0bzJpaVJnQ0dvS3ZNUWdWOXZIcmJvWEQ; + Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 08-Jun-2017 06:57:59 + GMT; secure', edgebucket=Tgg4R0Ag556DPxZZBR; Domain=reddit.com; Max-Age=63071999; + Path=/; secure] + Via: [1.1 varnish] + X-Cache: [MISS] + X-Cache-Hits: ['0'] + X-Moose: [majestic] + X-Served-By: [cache-ord1735-ORD] + X-Timer: ['S1496897879.401425,VS0,VE37'] + cache-control: ['max-age=0, must-revalidate'] + set-cookie: ['session_tracker=2Pb665rWf1oCGZfV3L.0.1496897879415.Z0FBQUFBQlpPTmxYQl9PVkQ5ekc5U3FDdWZ3cTZMRlRMWVMtZmdhd1duVGdLYTFva09aZmVIazNucGdHSV9tOV9qUy1FZ0M0RS15aWNYVDNxSWpzU1lOTnBQcGpoU19fY0JuM3NnczVyZ3JBcGhKYUQ0RzVPWXUyb0t4dkt3Y1kzRkRvWHg0TXFJbjI; + Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 08-Jun-2017 06:57:59 + GMT; secure', 'loid=000000000003fg1hbe.2.1496897879427.Z0FBQUFBQlpPTmxYX2xyMng2Mlk4bmVsUmt6bU04M01ZdTZmZGZ6a3JINnlqdnJKWjZINHNvUFNKc0FJUGVkU2NTem1ycXk3LW1WdXp1bERTM0RsU1J6MlFZZVNsRGZoOGFoREY2QU16ckpHQm01dHI0N2lES2JocmM2ZXVnVXphdFUzUi1yQ3VsQTQ; + Domain=reddit.com; Max-Age=63071999; Path=/; expires=Sat, 08-Jun-2019 04:57:59 + GMT; secure', 'session_tracker=vxk8Omjl7TIIb6u89z.0.1496897879423.Z0FBQUFBQlpPTmxYSmFBVDFlRG42OWZCN1V2R2QyVWlhd3VoaWxHWXBUZE9KRGpLSHZOWmhVVHVrTEdIaWQyWW04cXpnMG1TZ2JRUWRMMU5zc1JQQ21ta2NGcGx5RzZtZjNocmlrVEJzTmVuMnBER04ycWN0bzJpaVJnQ0dvS3ZNUWdWOXZIcmJvWEQ; + Domain=reddit.com; Max-Age=7199; Path=/; expires=Thu, 08-Jun-2017 06:57:59 + GMT; secure', edgebucket=Tgg4R0Ag556DPxZZBR; Domain=reddit.com; Max-Age=63071999; + Path=/; secure] + x-content-type-options: [nosniff] + x-frame-options: [SAMEORIGIN] + x-ua-compatible: [IE=edge] + x-xss-protection: [1; mode=block] + status: {code: 400, message: Bad Request} +version: 1 diff --git a/tests/test_oauth.py b/tests/test_oauth.py index efc87f4..94f3810 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import requests from rtv.oauth import OAuthHelper, OAuthHandler +from rtv.exceptions import InvalidRefreshToken from rtv.packages.praw.errors import OAuthException @@ -75,6 +76,15 @@ def test_oauth_terminal_mobile_authorize(reddit, terminal, config): assert '.compact' in oauth.reddit.config.API_PATHS['authorize'] +def test_oauth_authorize_invalid_token(oauth, terminal): + + oauth.config.refresh_token = 'invalid_token' + oauth.authorize() + assert oauth.server is None + assert oauth.config.refresh_token is None + assert isinstance(terminal.loader.exception, InvalidRefreshToken) + + def test_oauth_authorize_with_refresh_token(oauth, refresh_token): oauth.config.refresh_token = refresh_token