Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle exception on history unencrypted message #175

Merged
merged 3 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pubnub/models/consumer/history.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import binascii


class PNHistoryResult(object):
def __init__(self, messages, start_timetoken, end_timetoken):
self.messages = messages
Expand Down Expand Up @@ -44,12 +47,16 @@ def __init__(self, entry, crypto, timetoken=None, meta=None):
self.meta = meta
self.entry = entry
self.crypto = crypto
self.error = None

def __str__(self):
return "History item with tt: %s and content: %s" % (self.timetoken, self.entry)

def decrypt(self, cipher_key):
self.entry = self.crypto.decrypt(cipher_key, self.entry)
try:
self.entry = self.crypto.decrypt(cipher_key, self.entry)
except binascii.Error as e:
self.error = e


class PNFetchMessagesResult(object):
Expand Down
3 changes: 2 additions & 1 deletion pubnub/models/consumer/pubsub.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


class PNMessageResult(object):
def __init__(self, message, subscription, channel, timetoken, user_metadata=None, publisher=None):
def __init__(self, message, subscription, channel, timetoken, user_metadata=None, publisher=None, error=None):

if subscription is not None:
assert isinstance(subscription, str)
Expand All @@ -29,6 +29,7 @@ def __init__(self, message, subscription, channel, timetoken, user_metadata=None
self.timetoken = timetoken
self.user_metadata = user_metadata
self.publisher = publisher
self.error = error


class PNSignalMessageResult(PNMessageResult):
Expand Down
14 changes: 8 additions & 6 deletions pubnub/workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,23 @@ def _get_url_for_file_event_message(self, channel, extracted_message):

def _process_message(self, message_input):
if self._pubnub.config.cipher_key is None:
return message_input
return message_input, None
else:
try:
return self._pubnub.config.crypto.decrypt(
self._pubnub.config.cipher_key,
message_input
)
), None
except Exception as exception:
logger.warning("could not decrypt message: \"%s\", due to error %s" % (message_input, str(exception)))

pn_status = PNStatus()
pn_status.category = PNStatusCategory.PNDecryptionErrorCategory
pn_status.error_data = PNErrorData(str(exception), exception)
pn_status.error = True
pn_status.operation = PNOperationType.PNSubscribeOperation
self._listener_manager.announce_status(pn_status)
return message_input
return message_input, exception

def _process_incoming_payload(self, message):
assert isinstance(message, SubscribeMessage)
Expand Down Expand Up @@ -125,7 +126,7 @@ def _process_incoming_payload(self, message):
)
self._listener_manager.announce_membership(membership_result)
elif message.type == SubscribeMessageWorker.TYPE_FILE_MESSAGE:
extracted_message = self._process_message(message.payload)
extracted_message, _ = self._process_message(message.payload)
download_url = self._get_url_for_file_event_message(channel, extracted_message)

pn_file_result = PNFileMessageResult(
Expand All @@ -142,7 +143,7 @@ def _process_incoming_payload(self, message):
self._listener_manager.announce_file_message(pn_file_result)

else:
extracted_message = self._process_message(message.payload)
extracted_message, error = self._process_message(message.payload)
publisher = message.issuing_client_id

if extracted_message is None:
Expand Down Expand Up @@ -172,6 +173,7 @@ def _process_incoming_payload(self, message):
channel=channel,
subscription=subscription_match,
timetoken=publish_meta_data.publish_timetoken,
publisher=publisher
publisher=publisher,
error=error
)
self._listener_manager.announce_message(pn_message_result)
185 changes: 185 additions & 0 deletions tests/integrational/fixtures/native_sync/history/unencrypted.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
{
"version": 1,
"interactions": [
{
"request": {
"method": "DELETE",
"uri": "https://ps.pndsn.com/v3/history/sub-key/{PN_KEY_SUBSCRIBE}/channel/test_unencrypted",
"body": null,
"headers": {
"User-Agent": [
"PubNub-Python/7.3.0"
],
"Accept-Encoding": [
"gzip, deflate"
],
"Accept": [
"*/*"
],
"Connection": [
"keep-alive"
],
"Content-Length": [
"0"
]
}
},
"response": {
"status": {
"code": 200,
"message": "OK"
},
"headers": {
"Age": [
"0"
],
"Connection": [
"keep-alive"
],
"Content-Type": [
"text/javascript; charset=\"UTF-8\""
],
"Content-Length": [
"52"
],
"Cache-Control": [
"no-cache"
],
"Server": [
"Pubnub Storage"
],
"Date": [
"Wed, 22 Nov 2023 15:33:23 GMT"
],
"Access-Control-Allow-Origin": [
"*"
],
"Access-Control-Allow-Methods": [
"GET, POST, DELETE, OPTIONS"
],
"Accept-Ranges": [
"bytes"
]
},
"body": {
"string": "{\"status\": 200, \"error\": false, \"error_message\": \"\"}"
}
}
},
{
"request": {
"method": "GET",
"uri": "https://ps.pndsn.com/publish/{PN_KEY_PUBLISH}/{PN_KEY_SUBSCRIBE}/0/test_unencrypted/0/%22Lorem%20Ipsum%22?seqn=1",
"body": null,
"headers": {
"User-Agent": [
"PubNub-Python/7.3.0"
],
"Accept-Encoding": [
"gzip, deflate"
],
"Accept": [
"*/*"
],
"Connection": [
"keep-alive"
]
}
},
"response": {
"status": {
"code": 200,
"message": "OK"
},
"headers": {
"Connection": [
"keep-alive"
],
"Content-Type": [
"text/javascript; charset=\"UTF-8\""
],
"Content-Length": [
"30"
],
"Cache-Control": [
"no-cache"
],
"Date": [
"Wed, 22 Nov 2023 15:33:23 GMT"
],
"Access-Control-Allow-Origin": [
"*"
],
"Access-Control-Allow-Methods": [
"GET"
]
},
"body": {
"string": "[1,\"Sent\",\"17006672033304156\"]"
}
}
},
{
"request": {
"method": "GET",
"uri": "https://ps.pndsn.com/v2/history/sub-key/{PN_KEY_SUBSCRIBE}/channel/test_unencrypted?count=100",
"body": null,
"headers": {
"User-Agent": [
"PubNub-Python/7.3.0"
],
"Accept-Encoding": [
"gzip, deflate"
],
"Accept": [
"*/*"
],
"Connection": [
"keep-alive"
]
}
},
"response": {
"status": {
"code": 200,
"message": "OK"
},
"headers": {
"Age": [
"0"
],
"Connection": [
"keep-alive"
],
"Content-Type": [
"text/javascript; charset=\"UTF-8\""
],
"Content-Length": [
"53"
],
"Cache-Control": [
"no-cache"
],
"Server": [
"Pubnub Storage"
],
"Date": [
"Wed, 22 Nov 2023 15:33:25 GMT"
],
"Access-Control-Allow-Origin": [
"*"
],
"Access-Control-Allow-Methods": [
"GET, POST, DELETE, OPTIONS"
],
"Accept-Ranges": [
"bytes"
]
},
"body": {
"string": "[[\"Lorem Ipsum\"],17006672033304156,17006672033304156]"
}
}
}
]
}
30 changes: 29 additions & 1 deletion tests/integrational/native_sync/test_history.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import binascii
import logging
import time
import unittest
Expand All @@ -9,7 +10,7 @@
from pubnub.models.consumer.history import PNHistoryResult
from pubnub.models.consumer.pubsub import PNPublishResult
from pubnub.pubnub import PubNub
from tests.helper import pnconf_copy, pnconf_enc_copy, pnconf_pam_copy
from tests.helper import pnconf_copy, pnconf_enc_copy, pnconf_enc_env_copy, pnconf_env_copy, pnconf_pam_copy
from tests.integrational.vcr_helper import use_cassette_and_stub_time_sleep_native

pubnub.set_stream_logger('pubnub', logging.DEBUG)
Expand Down Expand Up @@ -104,3 +105,30 @@ def test_super_call_with_all_params(self):
assert isinstance(envelope.result, PNHistoryResult)

assert not envelope.status.is_error()


class TestHistoryCrypto(unittest.TestCase):
@use_cassette_and_stub_time_sleep_native('tests/integrational/fixtures/native_sync/history/unencrypted.json',
serializer='pn_json', filter_query_parameters=['uuid', 'pnsdk'])
def test_unencrypted(self):
ch = "test_unencrypted"
pubnub = PubNub(pnconf_env_copy())
pubnub.config.uuid = "history-native-sync-uuid"
pubnub.delete_messages().channel(ch).sync()
envelope = pubnub.publish().channel(ch).message("Lorem Ipsum").sync()
assert isinstance(envelope.result, PNPublishResult)
assert envelope.result.timetoken > 0

time.sleep(2)

pubnub_enc = PubNub(pnconf_enc_env_copy())
pubnub_enc.config.uuid = "history-native-sync-uuid"
envelope = pubnub_enc.history().channel(ch).sync()

assert isinstance(envelope.result, PNHistoryResult)
assert envelope.result.start_timetoken > 0
assert envelope.result.end_timetoken > 0
assert len(envelope.result.messages) == 1

assert envelope.result.messages[0].entry == 'Lorem Ipsum'
assert isinstance(envelope.result.messages[0].error, binascii.Error)
49 changes: 48 additions & 1 deletion tests/integrational/native_threads/test_subscribe.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import binascii
import logging
import unittest
import time
Expand All @@ -8,7 +9,7 @@
from pubnub.models.consumer.pubsub import PNPublishResult, PNMessageResult
from pubnub.pubnub import PubNub, SubscribeListener, NonSubscribeListener
from tests import helper
from tests.helper import pnconf_sub_copy
from tests.helper import pnconf_enc_env_copy, pnconf_env_copy, pnconf_sub_copy
from tests.integrational.vcr_helper import pn_vcr


Expand Down Expand Up @@ -253,3 +254,49 @@ def test_subscribe_cg_join_leave(self):

pubnub.stop()
pubnub_listener.stop()

def test_subscribe_pub_unencrypted_unsubscribe(self):
ch = helper.gen_channel("test-subscribe-sub-pub-unsub")

config_plain = pnconf_env_copy()
config_plain.enable_subscribe = True
pubnub_plain = PubNub(config_plain)

config = pnconf_enc_env_copy()
config.enable_subscribe = True
pubnub = PubNub(config)

subscribe_listener = SubscribeListener()
publish_operation = NonSubscribeListener()
message = "hey"

try:
pubnub.add_listener(subscribe_listener)

pubnub.subscribe().channels(ch).execute()
subscribe_listener.wait_for_connect()

pubnub_plain.publish().channel(ch).message(message).pn_async(publish_operation.callback)

if publish_operation.pn_await() is False:
self.fail("Publish operation timeout")

publish_result = publish_operation.result
assert isinstance(publish_result, PNPublishResult)
assert publish_result.timetoken > 0

result = subscribe_listener.wait_for_message_on(ch)
assert isinstance(result, PNMessageResult)
assert result.channel == ch
assert result.subscription is None
assert result.timetoken > 0
assert result.message == message
assert result.error is not None
assert isinstance(result.error, binascii.Error)

pubnub.unsubscribe().channels(ch).execute()
subscribe_listener.wait_for_disconnect()
except PubNubException as e:
self.fail(e)
finally:
pubnub.stop()