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

Commit

Permalink
feat: Add ChannelID report for UAID
Browse files Browse the repository at this point in the history
Adds new bridge system HTTP endpoint to fetch a list of server known
CHIDs for a given UAID. Useful for remote clients to check status. (see
documentation for calling structure and return).

Also fixed an edge case where clients that may have been forced offline
due to external system errors could still register new endpoints. The
behavior will now be to return a 410 error if a client has been flagged
as disconnected by the server. It is up to the client to check the local
bridge connection and re-establish any endpoints.

closes: #844, #843
  • Loading branch information
jrconlin committed Mar 23, 2017
1 parent 2e06521 commit 71035fa
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 4 deletions.
3 changes: 2 additions & 1 deletion autopush/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,8 @@ def all_channels(self, uaid):
# Functions that call store_message() would be required to
# update that list as well using register_channel()
try:
result = self.table.get_item(consistent=True, uaid=hasher(uaid),
result = self.table.get_item(consistent=True,
uaid=hasher(uaid),
chidmessageid=" ")
return (True, result["chids"] or set([]))
except ItemNotFound:
Expand Down
47 changes: 46 additions & 1 deletion autopush/tests/test_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def handle_finish(result):
return self.finish_deferred


CORS_HEAD = "POST,PUT,DELETE"
CORS_HEAD = "GET,POST,PUT,DELETE"


class RegistrationTestCase(unittest.TestCase):
Expand Down Expand Up @@ -591,6 +591,31 @@ def restore(*args, **kwargs):
chid=str(dummy_chid)))
return self.finish_deferred

def test_post_uaid_critical_failure(self, *args):
self.reg.request.body = json.dumps(dict(
type="webpush",
channelID=str(dummy_chid),
data={},
))
self.settings.router.get_uaid = Mock()
self.settings.router.get_uaid.return_value = {
"critical_failure": "Client is unreachable due to a configuration "
"error."
}
self.fernet_mock.configure_mock(**{
'encrypt.return_value': 'abcd123',
})

def handle_finish(value):
self._check_error(410, 105, "")

self.finish_deferred.addCallback(handle_finish)
self.reg.request.headers["Authorization"] = self.auth
self.reg.post(self._make_req(router_type="simplepush",
uaid=dummy_uaid.hex,
chid=str(dummy_chid)))
return self.finish_deferred

def test_post_nochid(self):
self.reg.request.body = json.dumps(dict(
type="simplepush",
Expand Down Expand Up @@ -834,3 +859,23 @@ def handle_finish(value):
self.finish_deferred.addCallback(handle_finish)
self.reg.delete(self._make_req("invalid", "test", dummy_uaid.hex))
return self.finish_deferred

def test_get(self):
self.reg.request.headers['Authorization'] = self.auth
chids = [str(dummy_chid), str(dummy_uaid)]

def handle_finish(value):
call_args = json.loads(
self.reg.write.call_args[0][0]
)
eq_(chids, call_args['channelIDs'])
eq_(dummy_uaid.hex, call_args['uaid'])

self.finish_deferred.addCallback(handle_finish)
self.settings.message.all_channels = Mock()
self.settings.message.all_channels.return_value = (True, chids)
self.reg.get(self._make_req(
router_type="test",
router_token="test",
uaid=dummy_uaid.hex))
return self.finish_deferred
40 changes: 38 additions & 2 deletions autopush/web/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,27 @@ def extract_data(self, req):
chid = req['path_kwargs'].get('chid', params.get("channelID"))
if uaid:
try:
uuid.UUID(uaid)
u_uuid = uuid.UUID(uaid)
except (ValueError, TypeError):
raise InvalidRequest("Invalid Request UAID",
status_code=401, errno=109)
# Check if the UAID has a 'critical error' which means that it's
# probably invalid and should be reset/re-registered
try:
record = self.context['settings'].router.get_uaid(u_uuid.hex)
if record.get('critical_failure'):
raise InvalidRequest("Invalid Request UAID",
status_code=410, errno=105)
except ItemNotFound:
pass

if chid:
try:
uuid.UUID(chid)
except (ValueError, TypeError):
raise InvalidRequest("Invalid Request Channel_id",
status_code=410, errno=106)

return dict(
auth=req.get('headers', {}).get("Authorization"),
params=params,
Expand Down Expand Up @@ -121,7 +132,7 @@ def validate_data(self, data):

class RegistrationHandler(BaseWebHandler):
"""Handle the Bridge services endpoints"""
cors_methods = "POST,PUT,DELETE"
cors_methods = "GET,POST,PUT,DELETE"

#############################################################
# Cyclone HTTP Methods
Expand Down Expand Up @@ -215,6 +226,22 @@ def _register_channel(self, router_data=None):
self.app_server_key)
return endpoint, router_data

@threaded_validate(RegistrationSchema)
def get(self, *args, **kwargs):
"""HTTP GET
Return a list of known channelIDs for a given UAID
"""
self.uaid = self.valid_input['uaid']
self.add_header("Content-Type", "application/json")
d = deferToThread(self.ap_settings.message.all_channels,
str(self.uaid))
d.addCallback(self._write_channels)
d.addErrback(self._response_err)
d.addErrback(self._uaid_not_found_err)
return d

@threaded_validate(RegistrationSchema)
def delete(self, *args, **kwargs):
"""HTTP DELETE
Expand Down Expand Up @@ -307,6 +334,15 @@ def _return_endpoint(self, endpoint_data, new_uaid, router=None):
client_info=self._client_info)
self.finish()

def _write_channels(self, channel_info, *args, **kwargs):
# channel_info is a tuple containing a flag and the list of channels
dashed = [str(uuid.UUID(x)) for x in channel_info[1]]
self.write(json.dumps(
{"uaid": self.uaid.hex,
"channelIDs": dashed}
))
self.finish()

def _success(self, result):
"""Writes out empty 200 response"""
self.write({})
Expand Down
41 changes: 41 additions & 0 deletions docs/http.rst
Original file line number Diff line number Diff line change
Expand Up @@ -509,4 +509,45 @@ Remove a given ChannelID subscription from a UAID.
**Return Codes:**

See :ref:`errors`.

Get Known Channels for a UAID
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Fetch the known ChannelIDs for a given bridged endpoint. This is useful to check link status.
If no channelIDs are present for a given UAID, an empty set of channelIDs will be returned.

**Call:**

.. http:get:: /v1/{type}/{app_id}/registration/{UAID}/
Authorization: Bearer {secret}

**Parameters:**

{}

**Reply:**

.. code-block:: json
{"uaid": {UAID}, "channelIDs": [{ChannelID}, ...]}
example:

.. code-block:: http
> GET /v1/gcm/33clienttoken33/registration/abcdef012345/
> Authorization: Bearer 00secret00
>
> {}
.. code-block:: json
< {"uaid": "abcdef012345",
< "channelIDS": ["01234567-0000-1111-2222-0123456789ab", "76543210-0000-1111-2222-0123456789ab"]}
**Return Codes:**


See :ref:`errors`.

0 comments on commit 71035fa

Please sign in to comment.