Skip to content

Commit

Permalink
Immutable locking config
Browse files Browse the repository at this point in the history
  • Loading branch information
seba-aln committed Jul 30, 2024
1 parent c3c573d commit de70a2f
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 3 deletions.
20 changes: 20 additions & 0 deletions pubnub/pnconfiguration.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import warnings
from typing import Any
from Cryptodome.Cipher import AES
from pubnub.enums import PNHeartbeatNotificationOptions, PNReconnectionPolicy
from pubnub.exceptions import PubNubException
Expand All @@ -12,6 +14,7 @@ class PNConfiguration(object):
RECONNECTION_MIN_EXPONENTIAL_BACKOFF = 1
RECONNECTION_MAX_EXPONENTIAL_BACKOFF = 32
DEFAULT_CRYPTO_MODULE = LegacyCryptoModule
_locked = False

def __init__(self):
# TODO: add validation
Expand Down Expand Up @@ -48,6 +51,8 @@ def __init__(self):
self.cryptor = None
self.file_cryptor = None
self._crypto_module = None
self.disable_config_locking = False
self._locked = False

def validate(self):
PNConfiguration.validate_not_empty_string(self.uuid)
Expand Down Expand Up @@ -168,3 +173,18 @@ def user_id(self):
def user_id(self, user_id):
PNConfiguration.validate_not_empty_string(user_id)
self._uuid = user_id

def lock(self):
self.__dict__['_locked'] = False if self.disable_config_locking else True

def __setattr__(self, name: str, value: Any) -> None:
if self._locked:
warnings.warn(UserWarning('Configuration is locked. Any changes made won\'t have any effect'))
return
if name in ['uuid', 'user_id']:
PNConfiguration.validate_not_empty_string(value)
self.__dict__[f'_{name}'] = value
elif name in ['cipher_mode', 'fallback_cipher_mode', 'crypto_module']:
self.__dict__[f'_{name}'] = value
else:
self.__dict__[name] = value
5 changes: 4 additions & 1 deletion pubnub/pubnub_core.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import time
from warnings import warn
from copy import deepcopy
from pubnub.endpoints.entities.membership.add_memberships import AddSpaceMembers, AddUserSpaces
from pubnub.endpoints.entities.membership.update_memberships import UpdateSpaceMembers, UpdateUserSpaces
from pubnub.endpoints.entities.membership.fetch_memberships import FetchSpaceMemberships, FetchUserMemberships
Expand Down Expand Up @@ -99,7 +100,9 @@ class PubNubCore:
_subscription_registry: PNSubscriptionRegistry

def __init__(self, config):
self.config = config
config.lock()
self.config = deepcopy(config)
self.config.lock()
self.config.validate()
self.headers = {
'User-Agent': self.sdk_name
Expand Down
14 changes: 14 additions & 0 deletions tests/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@
pnconf.subscribe_key = sub_key
pnconf.enable_subscribe = False
pnconf.uuid = uuid_mock
pnconf.disable_config_locking = True

pnconf_sub = PNConfiguration()
pnconf_sub.publish_key = pub_key
pnconf_sub.subscribe_request_timeout = 10
pnconf_sub.subscribe_key = sub_key
pnconf_sub.uuid = uuid_mock
pnconf_sub.disable_config_locking = True
pnconf_sub.enable_presence_heartbeat = True
pnconf_sub.set_presence_timeout_with_custom_interval(30, 10)

Expand All @@ -70,13 +72,15 @@
pnconf_enc.cipher_key = "testKey"
pnconf_enc.enable_subscribe = False
pnconf_enc.uuid = uuid_mock
pnconf_enc.disable_config_locking = True

pnconf_enc_sub = PNConfiguration()
pnconf_enc_sub.publish_key = pub_key
pnconf_enc_sub.subscribe_request_timeout = 10
pnconf_enc_sub.subscribe_key = sub_key
pnconf_enc_sub.cipher_key = "testKey"
pnconf_enc_sub.uuid = uuid_mock
pnconf_enc_sub.disable_config_locking = True

pnconf_pam = PNConfiguration()
pnconf_pam.publish_key = pub_key_pam
Expand All @@ -85,6 +89,7 @@
pnconf_pam.secret_key = sec_key_pam
pnconf_pam.enable_subscribe = False
pnconf_pam.uuid = uuid_mock
pnconf_pam.disable_config_locking = True


pnconf_pam_stub = PNConfiguration()
Expand All @@ -93,38 +98,44 @@
pnconf_pam_stub.subscribe_key = "sub-c-stub"
pnconf_pam_stub.secret_key = "sec-c-stub"
pnconf_pam_stub.uuid = uuid_mock
pnconf_pam_stub.disable_config_locking = True

pnconf_ssl = PNConfiguration()
pnconf_ssl.publish_key = pub_key
pnconf_ssl.subscribe_request_timeout = 10
pnconf_ssl.subscribe_key = sub_key
pnconf_ssl.ssl = True
pnconf_ssl.uuid = uuid_mock
pnconf_ssl.disable_config_locking = True

message_count_config = PNConfiguration()
message_count_config.publish_key = 'demo-36'
message_count_config.subscribe_request_timeout = 10
message_count_config.subscribe_key = 'demo-36'
message_count_config.origin = 'balancer1g.bronze.aws-pdx-1.ps.pn'
message_count_config.uuid = uuid_mock
message_count_config.disable_config_locking = True

pnconf_demo = PNConfiguration()
pnconf_demo.publish_key = 'demo'
pnconf_demo.subscribe_request_timeout = 10
pnconf_demo.subscribe_key = 'demo'
pnconf_demo.uuid = uuid_mock
pnconf_demo.disable_config_locking = True

file_upload_config = PNConfiguration()
file_upload_config.publish_key = pub_key_mock
file_upload_config.subscribe_request_timeout = 10
file_upload_config.subscribe_key = sub_key_mock
file_upload_config.uuid = uuid_mock
file_upload_config.disable_config_locking = True

mocked_config = PNConfiguration()
mocked_config.publish_key = pub_key_mock
mocked_config.subscribe_request_timeout = 10
mocked_config.subscribe_key = sub_key_mock
mocked_config.uuid = uuid_mock
mocked_config.disable_config_locking = True

hardcoded_iv_config = PNConfiguration()
hardcoded_iv_config.use_random_initialization_vector = False
Expand All @@ -137,6 +148,7 @@
pnconf_env.subscribe_key = os.environ.get('PN_KEY_SUBSCRIBE')
pnconf_env.enable_subscribe = False
pnconf_env.uuid = uuid_mock
pnconf_env.disable_config_locking = True

# configuration with keys from PN_KEY_* (enabled all except PAM, PUSH and FUNCTIONS) and encryption enabled
pnconf_enc_env = PNConfiguration()
Expand All @@ -146,6 +158,7 @@
pnconf_enc_env.cipher_key = "testKey"
pnconf_enc_env.enable_subscribe = False
pnconf_enc_env.uuid = uuid_mock
pnconf_enc_env.disable_config_locking = True

# configuration with keys from PN_KEY_PAM_* (enabled with all including PAM except PUSH and FUNCTIONS)
pnconf_pam_env = PNConfiguration()
Expand All @@ -155,6 +168,7 @@
pnconf_pam_env.secret_key = os.environ.get('PN_KEY_PAM_SECRET')
pnconf_pam_env.enable_subscribe = False
pnconf_pam_env.uuid = uuid_mock
pnconf_pam_env.disable_config_locking = True


def copy_and_update(config, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion tests/integrational/fixtures/native_sync/signal/uuid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ interactions:
User-Agent:
- PubNub-Python/4.1.0
method: GET
uri: https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=new-uuid
uri: https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock
response:
body:
string: '[1,"Sent","15640049765289377"]'
Expand Down
70 changes: 70 additions & 0 deletions tests/integrational/fixtures/native_sync/signal/uuid_no_lock.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
User-Agent:
- PubNub-Python/8.0.0
method: GET
uri: https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock
response:
body:
string: '[1,"Sent","17223715358009123"]'
headers:
Access-Control-Allow-Methods:
- GET
Access-Control-Allow-Origin:
- '*'
Cache-Control:
- no-cache
Connection:
- keep-alive
Content-Length:
- '30'
Content-Type:
- text/javascript; charset="UTF-8"
Date:
- Tue, 30 Jul 2024 20:32:15 GMT
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
User-Agent:
- PubNub-Python/8.0.0
method: GET
uri: https://ps.pndsn.com/signal/demo/demo/0/unique_sync/0/%22test%22?uuid=uuid-mock
response:
body:
string: '[1,"Sent","17223715359483475"]'
headers:
Access-Control-Allow-Methods:
- GET
Access-Control-Allow-Origin:
- '*'
Cache-Control:
- no-cache
Connection:
- keep-alive
Content-Length:
- '30'
Content-Type:
- text/javascript; charset="UTF-8"
Date:
- Tue, 30 Jul 2024 20:32:15 GMT
status:
code: 200
message: OK
version: 1
24 changes: 23 additions & 1 deletion tests/integrational/native_sync/test_change_uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,29 @@
@pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/signal/uuid.yaml',
filter_query_parameters=['seqn', 'pnsdk'])
def test_change_uuid():
with pytest.warns(UserWarning):
pnconf = pnconf_demo_copy()
pnconf.disable_config_locking = False
pn = PubNub(pnconf)

chan = 'unique_sync'
envelope = pn.signal().channel(chan).message('test').sync()

pnconf.uuid = 'new-uuid'
envelope = pn.signal().channel(chan).message('test').sync()

assert isinstance(envelope, Envelope)
assert not envelope.status.is_error()
assert envelope.result.timetoken == '15640049765289377'
assert isinstance(envelope.result, PNSignalResult)
assert isinstance(envelope.status, PNStatus)


@pn_vcr.use_cassette('tests/integrational/fixtures/native_sync/signal/uuid_no_lock.yaml',
filter_query_parameters=['seqn', 'pnsdk'])
def test_change_uuid_no_lock():
pnconf = pnconf_demo_copy()
pnconf.disable_config_locking = True
pn = PubNub(pnconf)

chan = 'unique_sync'
Expand All @@ -23,7 +45,7 @@ def test_change_uuid():

assert isinstance(envelope, Envelope)
assert not envelope.status.is_error()
assert envelope.result.timetoken == '15640049765289377'
assert envelope.result.timetoken == '17223715359483475'
assert isinstance(envelope.result, PNSignalResult)
assert isinstance(envelope.status, PNStatus)

Expand Down

0 comments on commit de70a2f

Please sign in to comment.