Only refresh token on unauthorized response from request
This commit is contained in:
+32
-14
@@ -5,7 +5,6 @@ from requests import Request, Session, exceptions
|
|||||||
from blinkpy import api
|
from blinkpy import api
|
||||||
from blinkpy.helpers import util
|
from blinkpy.helpers import util
|
||||||
from blinkpy.helpers.constants import BLINK_URL, LOGIN_ENDPOINT
|
from blinkpy.helpers.constants import BLINK_URL, LOGIN_ENDPOINT
|
||||||
from blinkpy.helpers import errors as ERROR
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -95,7 +94,10 @@ class Auth:
|
|||||||
self.token = self.login_response["authtoken"]["authtoken"]
|
self.token = self.login_response["authtoken"]["authtoken"]
|
||||||
self.client_id = self.login_response["client"]["id"]
|
self.client_id = self.login_response["client"]["id"]
|
||||||
self.account_id = self.login_response["account"]["id"]
|
self.account_id = self.login_response["account"]["id"]
|
||||||
except KeyError:
|
except LoginError:
|
||||||
|
_LOGGER.error("Login endpoint failed. Try again later.")
|
||||||
|
raise TokenRefreshFailed
|
||||||
|
except (TypeError, KeyError):
|
||||||
_LOGGER.error("Malformed login response: %s", self.login_response)
|
_LOGGER.error("Malformed login response: %s", self.login_response)
|
||||||
raise TokenRefreshFailed
|
raise TokenRefreshFailed
|
||||||
return True
|
return True
|
||||||
@@ -110,11 +112,12 @@ class Auth:
|
|||||||
"""Check for valid response."""
|
"""Check for valid response."""
|
||||||
if not json_resp:
|
if not json_resp:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
json_data = response.json()
|
if response.status_code in [101, 401]:
|
||||||
if json_data["code"] in ERROR.BLINK_ERRORS:
|
raise UnauthorizedError
|
||||||
|
if response.status_code == 404:
|
||||||
raise exceptions.ConnectionError
|
raise exceptions.ConnectionError
|
||||||
|
json_data = response.json()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
@@ -141,31 +144,42 @@ class Auth:
|
|||||||
:param reqtype: Can be 'get' or 'post' (default: 'get')
|
:param reqtype: Can be 'get' or 'post' (default: 'get')
|
||||||
:param stream: Stream response? True/FALSE
|
:param stream: Stream response? True/FALSE
|
||||||
:param json_resp: Return JSON response? TRUE/False
|
:param json_resp: Return JSON response? TRUE/False
|
||||||
:param is_retry: Is this a retry attempt? True/FALSE
|
:param is_retry: Is this part of a re-auth attempt? True/FALSE
|
||||||
"""
|
"""
|
||||||
req = self.prepare_request(url, headers, data, reqtype)
|
req = self.prepare_request(url, headers, data, reqtype)
|
||||||
try:
|
try:
|
||||||
response = self.session.send(req, stream=stream)
|
response = self.session.send(req, stream=stream)
|
||||||
return self.validate_response(response, json_resp)
|
return self.validate_response(response, json_resp)
|
||||||
|
except (exceptions.ConnectionError, exceptions.Timeout):
|
||||||
except (exceptions.ConnectionError, exceptions.Timeout, TokenRefreshFailed):
|
_LOGGER.error(
|
||||||
|
"Connection error. Endpoint %s possibly down or throttled. %s: %s",
|
||||||
|
url,
|
||||||
|
response.status_code,
|
||||||
|
response.reason,
|
||||||
|
)
|
||||||
|
except BlinkBadResponse:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Expected json response from %s, but received: %s: %s",
|
||||||
|
url,
|
||||||
|
response.status_code,
|
||||||
|
response.reason,
|
||||||
|
)
|
||||||
|
except UnauthorizedError:
|
||||||
try:
|
try:
|
||||||
if not is_retry:
|
if not is_retry:
|
||||||
self.refresh_token()
|
self.refresh_token()
|
||||||
return self.query(
|
return self.query(
|
||||||
url=url,
|
url=url,
|
||||||
data=data,
|
data=data,
|
||||||
headers=headers,
|
headers=self.header,
|
||||||
reqtype=reqtype,
|
reqtype=reqtype,
|
||||||
stream=stream,
|
stream=stream,
|
||||||
json_resp=json_resp,
|
json_resp=json_resp,
|
||||||
is_retry=True,
|
is_retry=True,
|
||||||
)
|
)
|
||||||
except (TokenRefreshFailed, LoginError):
|
_LOGGER.error("Unable to access %s after token refresh.", url)
|
||||||
_LOGGER.error("Endpoint %s failed. Unable to refresh login tokens", url)
|
except TokenRefreshFailed:
|
||||||
except BlinkBadResponse:
|
_LOGGER.error("Unable to refresh token.")
|
||||||
_LOGGER.error("Expected json response, but received: %s", response)
|
|
||||||
_LOGGER.error("Endpoint %s failed", url)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def send_auth_key(self, blink, key):
|
def send_auth_key(self, blink, key):
|
||||||
@@ -200,3 +214,7 @@ class LoginError(Exception):
|
|||||||
|
|
||||||
class BlinkBadResponse(Exception):
|
class BlinkBadResponse(Exception):
|
||||||
"""Class to throw bad json response exception."""
|
"""Class to throw bad json response exception."""
|
||||||
|
|
||||||
|
|
||||||
|
class UnauthorizedError(Exception):
|
||||||
|
"""Class to throw an unauthorized access error."""
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ class Blink:
|
|||||||
"""
|
"""
|
||||||
if self.check_if_ok_to_update() or force:
|
if self.check_if_ok_to_update() or force:
|
||||||
if not self.available:
|
if not self.available:
|
||||||
self.auth.refresh_token()
|
|
||||||
self.setup_post_verify()
|
self.setup_post_verify()
|
||||||
|
|
||||||
for sync_name, sync_module in self.sync.items():
|
for sync_name, sync_module in self.sync.items():
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ AUTH_TOKEN = (
|
|||||||
)
|
)
|
||||||
REQUEST = (4, "Cannot perform request (get/post type incorrect)")
|
REQUEST = (4, "Cannot perform request (get/post type incorrect)")
|
||||||
|
|
||||||
BLINK_ERRORS = [101, 400, 404]
|
BLINK_ERRORS = [400, 404]
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class MockResponse:
|
|||||||
self.json_data = json_data
|
self.json_data = json_data
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
self.raw_data = raw_data
|
self.raw_data = raw_data
|
||||||
|
self.reason = "foobar"
|
||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
"""Return json data from get_request."""
|
"""Return json data from get_request."""
|
||||||
|
|||||||
+26
-2
@@ -3,7 +3,13 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from requests import exceptions
|
from requests import exceptions
|
||||||
from blinkpy.auth import Auth, LoginError, TokenRefreshFailed, BlinkBadResponse
|
from blinkpy.auth import (
|
||||||
|
Auth,
|
||||||
|
LoginError,
|
||||||
|
TokenRefreshFailed,
|
||||||
|
BlinkBadResponse,
|
||||||
|
UnauthorizedError,
|
||||||
|
)
|
||||||
import blinkpy.helpers.constants as const
|
import blinkpy.helpers.constants as const
|
||||||
import tests.mock_responses as mresp
|
import tests.mock_responses as mresp
|
||||||
|
|
||||||
@@ -90,6 +96,10 @@ class TestAuth(unittest.TestCase):
|
|||||||
with self.assertRaises(exceptions.ConnectionError):
|
with self.assertRaises(exceptions.ConnectionError):
|
||||||
self.auth.validate_response(fake_resp, True)
|
self.auth.validate_response(fake_resp, True)
|
||||||
|
|
||||||
|
fake_resp = mresp.MockResponse({"code": 101}, 401)
|
||||||
|
with self.assertRaises(UnauthorizedError):
|
||||||
|
self.auth.validate_response(fake_resp, True)
|
||||||
|
|
||||||
def test_good_response_code(self):
|
def test_good_response_code(self):
|
||||||
"""Check good response code from server."""
|
"""Check good response code from server."""
|
||||||
fake_resp = mresp.MockResponse({"foo": "bar"}, 200)
|
fake_resp = mresp.MockResponse({"foo": "bar"}, 200)
|
||||||
@@ -133,6 +143,8 @@ class TestAuth(unittest.TestCase):
|
|||||||
mock_req.return_value = fake_resp
|
mock_req.return_value = fake_resp
|
||||||
with self.assertRaises(LoginError):
|
with self.assertRaises(LoginError):
|
||||||
self.auth.login()
|
self.auth.login()
|
||||||
|
with self.assertRaises(TokenRefreshFailed):
|
||||||
|
self.auth.refresh_token()
|
||||||
|
|
||||||
@mock.patch("blinkpy.auth.Auth.login")
|
@mock.patch("blinkpy.auth.Auth.login")
|
||||||
def test_refresh_token(self, mock_login):
|
def test_refresh_token(self, mock_login):
|
||||||
@@ -197,10 +209,22 @@ class TestAuth(unittest.TestCase):
|
|||||||
def test_query_retry(self, mock_refresh, mock_validate):
|
def test_query_retry(self, mock_refresh, mock_validate):
|
||||||
"""Check handling of request retry."""
|
"""Check handling of request retry."""
|
||||||
self.auth.session = MockSession()
|
self.auth.session = MockSession()
|
||||||
mock_validate.side_effect = [TokenRefreshFailed, "foobar"]
|
mock_validate.side_effect = [UnauthorizedError, "foobar"]
|
||||||
mock_refresh.return_value = True
|
mock_refresh.return_value = True
|
||||||
self.assertEqual(self.auth.query(url="http://example.com"), "foobar")
|
self.assertEqual(self.auth.query(url="http://example.com"), "foobar")
|
||||||
|
|
||||||
|
@mock.patch("blinkpy.auth.Auth.validate_response")
|
||||||
|
@mock.patch("blinkpy.auth.Auth.refresh_token")
|
||||||
|
def test_query_retry_failed(self, mock_refresh, mock_validate):
|
||||||
|
"""Check handling of failed retry request."""
|
||||||
|
self.auth.seession = MockSession()
|
||||||
|
mock_validate.side_effect = [UnauthorizedError, BlinkBadResponse]
|
||||||
|
mock_refresh.return_value = True
|
||||||
|
self.assertEqual(self.auth.query(url="http://example.com"), None)
|
||||||
|
|
||||||
|
mock_validate.side_effect = [UnauthorizedError, TokenRefreshFailed]
|
||||||
|
self.assertEqual(self.auth.query(url="http://example.com"), None)
|
||||||
|
|
||||||
|
|
||||||
class MockSession:
|
class MockSession:
|
||||||
"""Object to mock a session."""
|
"""Object to mock a session."""
|
||||||
|
|||||||
Reference in New Issue
Block a user