Skip to content
This repository has been archived by the owner on Jul 13, 2023. It is now read-only.

Commit

Permalink
Merge pull request #396 from mozilla-services/bug/issue-395
Browse files Browse the repository at this point in the history
Bug/issue 395
  • Loading branch information
bbangert committed Mar 10, 2016
2 parents 42b1c9d + 86ba66d commit ea53a87
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 42 deletions.
2 changes: 1 addition & 1 deletion autopush/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.13' # pragma: nocover
__version__ = '1.13.1' # pragma: nocover
10 changes: 6 additions & 4 deletions autopush/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
hasher,
normalize_id,
)
from autopush.exceptions import InvalidTokenException
from autopush.router.interface import RouterException
from autopush.utils import (
generate_hash,
Expand Down Expand Up @@ -268,7 +269,7 @@ def _uaid_not_found_err(self, fail):

def _token_err(self, fail):
"""errBack for token decryption fail"""
fail.trap(InvalidToken, ValueError)
fail.trap(InvalidToken, InvalidTokenException)
log.msg("Invalid token", **self._client_info)
self._write_response(400, 102)

Expand Down Expand Up @@ -354,11 +355,11 @@ def _token_valid(self, result, func):
function"""
info = result.split(":")
if len(info) != 3:
raise ValueError("Wrong message token components")
raise InvalidTokenException("Wrong message token components")

kind, uaid, chid = info
if kind != 'm':
raise ValueError("Wrong message token kind")
raise InvalidTokenException("Wrong message token kind")
return func(kind, uaid, chid)

@cyclone.web.asynchronous
Expand Down Expand Up @@ -409,6 +410,7 @@ def put(self, api_ver="v0", token=None):
Primary entry-point to handling a notification for a push client.
"""
api_ver = api_ver or "v0"
self.start_time = time.time()
public_key = None
keys = {}
Expand Down Expand Up @@ -436,7 +438,7 @@ def put(self, api_ver="v0", token=None):
def _token_valid(self, result):
"""Called after the token is decrypted successfully"""
if len(result) != 2:
raise ValueError("Wrong subscription token components")
raise InvalidTokenException("Wrong subscription token components")

self.uaid, self.chid = result
d = deferToThread(self.ap_settings.router.get_uaid, self.uaid)
Expand Down
4 changes: 4 additions & 0 deletions autopush/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@

class AutopushException(Exception):
"""Parent Autopush Exception"""


class InvalidTokenException(Exception):
"""Invalid URL token Exception"""
11 changes: 6 additions & 5 deletions autopush/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
Router,
Message
)
from autopush.exceptions import InvalidTokenException
from autopush.metrics import (
DatadogMetrics,
TwistedMetrics,
Expand Down Expand Up @@ -293,16 +294,16 @@ def parse_endpoint(self, token, version="v0", public_key=None):

if version == 'v0':
if ':' not in token:
raise ValueError("Corrupted push token")
raise InvalidTokenException("Corrupted push token")
return tuple(token.split(':'))
if version == 'v1' and len(token) != 32:
raise ValueError("Corrupted push token")
raise InvalidTokenException("Corrupted push token")
if version == 'v2':
if len(token) != 64:
raise ValueError("Corrupted push token")
raise InvalidTokenException("Corrupted push token")
if not public_key:
raise ValueError("Invalid key data")
raise InvalidTokenException("Invalid key data")
if not constant_time.bytes_eq(sha256(public_key).digest(),
token[32:]):
raise ValueError("Key mismatch")
raise InvalidTokenException("Key mismatch")
return (token[:16].encode('hex'), token[16:32].encode('hex'))
63 changes: 33 additions & 30 deletions autopush/tests/test_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from twisted.web.client import Agent, Response
from txstatsd.metrics.metrics import Metrics


import autopush.endpoint as endpoint
import autopush.utils as utils
from autopush.db import (
Expand All @@ -33,6 +34,7 @@
has_connected_this_month,
hasher
)
from autopush.exceptions import InvalidTokenException
from autopush.settings import AutopushSettings
from autopush.router.interface import IRouter, RouterResponse
from autopush.senderids import SenderIDs
Expand All @@ -41,6 +43,7 @@
mock_dynamodb2 = mock_dynamodb2()
dummy_uaid = str(uuid.UUID("abad1dea00000000aabbccdd00000000"))
dummy_chid = str(uuid.UUID("deadbeef00000000decafbad00000000"))
dummy_token = dummy_uaid + ":" + dummy_chid


def setUp():
Expand Down Expand Up @@ -307,7 +310,7 @@ def handle_finish(value):

def test_webpush_missing_ttl_user_offline(self):
from autopush.router.interface import RouterException
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
del(self.request_mock.headers["ttl"])
self.request_mock.headers["encryption"] = "stuff"
Expand Down Expand Up @@ -508,7 +511,7 @@ def test_load_params_prefer_body(self, t):
eq_(data, 'bai')

def test_put_data_too_large(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.ap_settings.router.get_uaid.return_value = {}
self.endpoint.ap_settings.max_data = 3
self.endpoint.request.body = b'version=1&data=1234'
Expand Down Expand Up @@ -576,7 +579,7 @@ def handle_finish(result):

self.endpoint.version, self.endpoint.data = 789, None

exc = self.assertRaises(ValueError,
exc = self.assertRaises(InvalidTokenException,
self.endpoint._token_valid,
['invalid'])
eq_(exc.message, "Wrong subscription token components")
Expand All @@ -585,7 +588,7 @@ def handle_finish(result):
return self.finish_deferred

def test_put_default_router(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict()
self.sp_router_mock.route_notification.return_value = RouterResponse()

Expand All @@ -602,7 +605,7 @@ def test_put_router_with_headers(self):
self.request_mock.headers["content-encoding"] = 'text'
self.request_mock.headers["encryption-key"] = "encKey"
self.request_mock.body = b' '
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict(
router_type="webpush",
router_data=dict(),
Expand All @@ -621,7 +624,7 @@ def handle_finish(result):
return self.finish_deferred

def test_put_router_needs_change(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict(
router_type="simplepush",
router_data=dict(),
Expand All @@ -641,7 +644,7 @@ def handle_finish(result):
return self.finish_deferred

def test_put_router_needs_update(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict(
router_type="simplepush",
router_data=dict(),
Expand All @@ -666,7 +669,7 @@ def test_put_bogus_headers(self):
self.request_mock.headers["encryption-key"] = "encKey"
self.request_mock.headers["crypto-key"] = "fake=crypKey"
self.request_mock.body = b' '
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict(
router_type="webpush",
router_data=dict(),
Expand All @@ -690,7 +693,7 @@ def test_put_invalid_vapid_crypto_header(self):
self.request_mock.headers["authorization"] = "some auth"
self.request_mock.headers["crypto-key"] = "crypKey"
self.request_mock.body = b' '
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict(
router_type="webpush",
router_data=dict(),
Expand All @@ -714,7 +717,7 @@ def test_put_invalid_vapid_crypto_key(self):
self.request_mock.headers["authorization"] = "invalid"
self.request_mock.headers["crypto-key"] = "crypt=crap"
self.request_mock.body = b' '
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict(
router_type="webpush",
router_data=dict(),
Expand All @@ -738,7 +741,7 @@ def test_put_invalid_vapid_auth_header(self):
self.request_mock.headers["authorization"] = "invalid"
self.request_mock.headers["crypto-key"] = "p256ecdsa=crap"
self.request_mock.body = b' '
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict(
router_type="webpush",
router_data=dict(),
Expand All @@ -761,7 +764,7 @@ def test_put_missing_vapid_crypto_header(self):
self.request_mock.headers["content-encoding"] = 'text'
self.request_mock.headers["authorization"] = "some auth"
self.request_mock.body = b' '
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.return_value = dict(
router_type="webpush",
router_data=dict(),
Expand All @@ -780,7 +783,7 @@ def handle_finish(result):
return self.finish_deferred

def test_post_webpush_with_headers_in_response(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -811,7 +814,7 @@ def _gen_jwt(self, header, payload):
return (sig, crypto_key)

def test_post_webpush_with_vapid_auth(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -873,7 +876,7 @@ def test_decipher_public_key(self):
self.assertRaises(ValueError, decipher_public_key, crap[:60])

def test_post_webpush_with_other_than_vapid_auth(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -905,7 +908,7 @@ def handle_finish(result):
return self.finish_deferred

def test_post_webpush_with_bad_vapid_auth(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -937,7 +940,7 @@ def handle_finish(result):
return self.finish_deferred

def test_post_webpush_no_sig(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -983,7 +986,7 @@ def test_util_extract_jwt(self):
eq_(utils.extract_jwt(sig, crypto_key), payload)

def test_post_webpush_bad_sig(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -1017,7 +1020,7 @@ def handle_finish(result):
return self.finish_deferred

def test_post_webpush_bad_exp(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -1049,7 +1052,7 @@ def handle_finish(result):
return self.finish_deferred

def test_post_webpush_with_auth(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -1077,7 +1080,7 @@ def test_post_webpush_with_logged_delivered(self):
import autopush.endpoint
log_patcher = patch.object(autopush.endpoint.log, "msg", spec=True)
mock_log = log_patcher.start()
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -1108,7 +1111,7 @@ def test_post_webpush_with_logged_stored(self):
import autopush.endpoint
log_patcher = patch.object(autopush.endpoint.log, "msg", spec=True)
mock_log = log_patcher.start()
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -1138,7 +1141,7 @@ def handle_finish(result):
@patch("twisted.python.log")
def test_post_db_error_in_routing(self, mock_log):
from autopush.router.interface import RouterException
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.endpoint.set_header = Mock()
self.request_mock.headers["encryption"] = "stuff"
self.request_mock.headers["content-encoding"] = "aes128"
Expand Down Expand Up @@ -1167,7 +1170,7 @@ def handle_finish(result):
return self.finish_deferred

def test_put_db_error(self):
self.fernet_mock.decrypt.return_value = "123:456"
self.fernet_mock.decrypt.return_value = dummy_token
self.router_mock.get_uaid.side_effect = self._throw_provisioned_error

def handle_finish(result):
Expand Down Expand Up @@ -1281,13 +1284,13 @@ def test_parse_endpoint(self):

# v0 bad
self.fernet_mock.decrypt.return_value = v1_valid
exc = self.assertRaises(ValueError,
exc = self.assertRaises(InvalidTokenException,
self.settings.parse_endpoint,
'/invalid')
eq_(exc.message, 'Corrupted push token')

self.fernet_mock.decrypt.return_value = v1_valid[:30]
exc = self.assertRaises(ValueError,
exc = self.assertRaises(InvalidTokenException,
self.settings.parse_endpoint,
'invalid', 'v1')
eq_(exc.message, 'Corrupted push token')
Expand All @@ -1301,25 +1304,25 @@ def test_parse_endpoint(self):
eq_(tokens, (uaid_strip, chid_strip))

self.fernet_mock.decrypt.return_value = v1_valid + "invalid"
exc = self.assertRaises(ValueError,
exc = self.assertRaises(InvalidTokenException,
self.settings.parse_endpoint,
'invalid', 'v2', pub_key)
eq_(exc.message, "Corrupted push token")

self.fernet_mock.decrypt.return_value = v1_valid + v2_valid
exc = self.assertRaises(ValueError,
exc = self.assertRaises(InvalidTokenException,
self.settings.parse_endpoint,
'invalid', 'v2', pub_key[:30])
eq_(exc.message, "Key mismatch")

self.fernet_mock.decrypt.return_value = v1_valid + v2_invalid
exc = self.assertRaises(ValueError,
exc = self.assertRaises(InvalidTokenException,
self.settings.parse_endpoint,
'invalid', 'v2')
eq_(exc.message, "Invalid key data")

self.fernet_mock.decrypt.return_value = v1_valid + v2_invalid
exc = self.assertRaises(ValueError,
exc = self.assertRaises(InvalidTokenException,
self.settings.parse_endpoint,
'invalid', 'v2', pub_key)
eq_(exc.message, "Key mismatch")
Expand Down
Loading

0 comments on commit ea53a87

Please sign in to comment.