From 8e9e4893adf75f289f0e68137a357581ef6757a3 Mon Sep 17 00:00:00 2001 From: Maximilian Pilz Date: Mon, 28 Aug 2023 12:17:24 +0200 Subject: [PATCH] Add ssl context container (#216) ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist: - [x] I have updated the [changelog](../CHANGELOG.md) accordingly. - [x] I have added tests to cover my changes. --- .github/workflows/mypy.yml | 1 + .github/workflows/ruff.yml | 1 + .gitignore | 5 +- CHANGELOG.md | 17 +- examples/ReferenceTest/reference_consumer.py | 20 +- examples/ReferenceTest/reference_provider.py | 26 +- examples/ReferenceTest/test_reference.py | 2 +- src/sdc11073/certloader.py | 62 ++-- src/sdc11073/consumer/consumerimpl.py | 40 +-- src/sdc11073/httpserver/httpserverimpl.py | 2 +- src/sdc11073/provider/providerimpl.py | 20 +- tests/mockstuff.py | 9 +- tests/test_alertsignaldelegate.py | 2 +- tests/test_client_device.py | 303 ++++++++++++++++--- tests/test_client_waveform.py | 2 +- tests/test_compression.py | 2 +- tests/test_device_periodic_reports.py | 2 +- tests/test_operations.py | 24 +- tests/test_string_enum_descriptors.py | 2 +- tests/test_tutorial.py | 6 +- 20 files changed, 395 insertions(+), 153 deletions(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 44d48c78..51092be4 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -21,6 +21,7 @@ jobs: run: pip install ".[mypy]" - name: Run mypy + continue-on-error: true # remove when https://github.com/Draegerwerk/sdc11073/issues/156 is done uses: sasanquaneuf/mypy-github-action@releases/v1 with: checkName: 'mypy' # NOTE: this needs to be the same as the job name diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index b8e2bdb1..e1d2fe62 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -11,3 +11,4 @@ jobs: steps: - uses: actions/checkout@v3 - uses: chartboost/ruff-action@v1 + continue-on-error: true # remove when https://github.com/Draegerwerk/sdc11073/issues/156 is done diff --git a/.gitignore b/.gitignore index b0923a45..7493c7a8 100644 --- a/.gitignore +++ b/.gitignore @@ -127,4 +127,7 @@ venv.bak/ dmypy.json # Pyre type checker -.pyre/ \ No newline at end of file +.pyre/ + +# Intellij IDEA / PyCharm +.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ce59b4e..2d3cf633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## Fixed +### Added + +- `network` module to handle network adapter stuff of the host computer +- `mypy` static code analysis + +### Fixed + - possible choosing wrong ipaddress/network interface [#187](https://github.com/Draegerwerk/sdc11073/issues/187) - added missing SerialNumber to ThisDeviceType - no creation of operation target states, they should already exist or are not needed if multi state. @@ -18,11 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fixed a bug where the `SdcConsumer` failed to determine the host network adapter if the ip contained in the `device_location` is on a different subnet - comparison of extensions would fail [#238](https://github.com/Draegerwerk/sdc11073/issues/238) -## Added -- `network` module to handle network adapter stuff of the host computer -- `mypy` static code analysis -## Changed +### Changed + - when creating a `SdcClient` with a `device_location` or `WsDiscovery` containing an ip where no suitable host network adapter could be determined from, an `NetworkAdapterNotFoundError` is raised - removed `netconn` module - renamed Device with Provider in order to be more compliant with sdc11073 names: @@ -35,6 +39,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SdcLocation class reworked; use 'bldng' instead of 'bld', which better matches the standard. - Some classes renamed in pmtypes.py - soap client does not try implicit reconnects +- replaced some of the ssl_context parameters with an ssl_context_container parameter, + that can hold two ssl context objects, to be able to get rid of + the deprecation warning occurring when using the same ssl context for both client and server side ## [2.0.0a5] - 2023-06-27 diff --git a/examples/ReferenceTest/reference_consumer.py b/examples/ReferenceTest/reference_consumer.py index f4c9172e..64ff4b1b 100644 --- a/examples/ReferenceTest/reference_consumer.py +++ b/examples/ReferenceTest/reference_consumer.py @@ -7,7 +7,7 @@ from sdc11073 import commlog from sdc11073 import observableproperties -from sdc11073.certloader import mk_ssl_context_from_folder +from sdc11073.certloader import mk_ssl_contexts_from_folder from sdc11073.consumer import SdcConsumer from sdc11073.definitions_sdc import SDC_v1_Definitions from sdc11073.mdib.consumermdib import ConsumerMdib @@ -52,17 +52,17 @@ def run_ref_test(): print('Test step 2: connect to device...') try: if ca_folder: - ssl_context = mk_ssl_context_from_folder(ca_folder, - cyphers_file=None, - private_key='user_private_key_encrypted.pem', - certificate='user_certificate_root_signed.pem', - ca_public_key='root_certificate.pem', - ssl_passwd=ssl_passwd, - ) + ssl_context_container = mk_ssl_contexts_from_folder(ca_folder, + cyphers_file=None, + private_key='user_private_key_encrypted.pem', + certificate='user_certificate_root_signed.pem', + ca_public_key='root_certificate.pem', + ssl_passwd=ssl_passwd, + ) else: - ssl_context = None + ssl_context_container = None client = SdcConsumer.from_wsd_service(my_service, - ssl_context=ssl_context, + ssl_context_container=ssl_context_container, validate=True) client.start_all() print('Test step 2 successful: connected to device') diff --git a/examples/ReferenceTest/reference_provider.py b/examples/ReferenceTest/reference_provider.py index bfa7434e..4bbb21d6 100644 --- a/examples/ReferenceTest/reference_provider.py +++ b/examples/ReferenceTest/reference_provider.py @@ -10,8 +10,9 @@ import uuid from typing import TYPE_CHECKING +import sdc11073.certloader from sdc11073 import location, network, provider, wsdiscovery -from sdc11073.certloader import mk_ssl_context_from_folder +from sdc11073.certloader import mk_ssl_contexts_from_folder from sdc11073.loghelper import LoggerAdapter from sdc11073.mdib import ProviderMdib from sdc11073.provider.components import SdcProviderComponents @@ -22,7 +23,7 @@ from sdc11073.xml_types.dpws_types import ThisDeviceType, ThisModelType if TYPE_CHECKING: - import ssl + pass USE_REFERENCE_PARAMETERS = True @@ -42,16 +43,16 @@ def get_location() -> location.SdcLocation: bed=os.getenv('ref_bed', default='r_bed')) # noqa: SIM112 -def get_ssl_context() -> ssl.SSLContext | None: +def get_ssl_context() -> sdc11073.certloader.SSLContextContainer | None: """Get ssl context from environment or None.""" if (ca_folder := os.getenv('ref_ca')) is None: # noqa: SIM112 return None - return mk_ssl_context_from_folder(ca_folder, - private_key='user_private_key_encrypted.pem', - certificate='user_certificate_root_signed.pem', - ca_public_key='root_certificate.pem', - cyphers_file=None, - ssl_passwd=os.getenv('ref_ssl_passwd')) # noqa: SIM112 + return mk_ssl_contexts_from_folder(ca_folder, + private_key='user_private_key_encrypted.pem', + certificate='user_certificate_root_signed.pem', + ca_public_key='root_certificate.pem', + cyphers_file=None, + ssl_passwd=os.getenv('ref_ssl_passwd')) # noqa: SIM112 def get_epr() -> uuid.UUID: @@ -68,7 +69,7 @@ def create_reference_provider( dpws_device: dpws_types.ThisDeviceType | None = None, epr: uuid.UUID | None = None, specific_components: SdcProviderComponents | None = None, - ssl_context: ssl.SSLContext | None = None) -> provider.SdcProvider: + ssl_context_container: sdc11073.certloader.SSLContextContainer | None = None) -> provider.SdcProvider: # generic way to create a device, this what you usually do: ws_discovery = ws_discovery or wsdiscovery.WSDiscovery(get_network_adapter().ip) ws_discovery.start() @@ -89,7 +90,7 @@ def create_reference_provider( device_mdib_container=mdib, epr=epr or get_epr(), specific_components=specific_components, - ssl_context=ssl_context or get_ssl_context(), + ssl_context_container=ssl_context_container or get_ssl_context(), ) for desc in prov.mdib.descriptions.objects: desc.SafetyClassification = pm_types.SafetyClassification.MED_A @@ -151,7 +152,8 @@ def run_provider(): else: specific_components = None # provComponents(services_factory=mk_all_services_except_localization) - prov = create_reference_provider(specific_components=specific_components) + prov = create_reference_provider(ws_discovery=wsd, specific_components=specific_components) + set_reference_data(prov, get_location()) metric = prov.mdib.descriptions.handle.get_one('numeric.ch1.vmd0') alert_condition = prov.mdib.descriptions.handle.get_one('ac0.mds0') diff --git a/examples/ReferenceTest/test_reference.py b/examples/ReferenceTest/test_reference.py index f56431d2..ef5b5bd5 100644 --- a/examples/ReferenceTest/test_reference.py +++ b/examples/ReferenceTest/test_reference.py @@ -137,7 +137,7 @@ def _runtest_client_connects(self): print('Test step 1 successful: device discovered') print('Test step 2: connect to device...') - client = SdcConsumer.from_wsd_service(my_service, ssl_context=None) + client = SdcConsumer.from_wsd_service(my_service, ssl_context_container=None) self.my_clients.append(client) client.start_all() self.assertTrue(client.is_connected) diff --git a/src/sdc11073/certloader.py b/src/sdc11073/certloader.py index c99b1790..ce1c9ec0 100644 --- a/src/sdc11073/certloader.py +++ b/src/sdc11073/certloader.py @@ -1,23 +1,30 @@ +import dataclasses import os import ssl -def mk_ssl_context_from_folder(ca_folder, - private_key='userkey.pem', - certificate='usercert.pem', - ca_public_key='cacert.pem', - cyphers_file=None, - ssl_passwd=None) -> ssl.SSLContext: +@dataclasses.dataclass +class SSLContextContainer: + client_context: ssl.SSLContext + server_context: ssl.SSLContext + + +def mk_ssl_contexts_from_folder(ca_folder, + private_key='userkey.pem', + certificate='usercert.pem', + ca_public_key='cacert.pem', + cyphers_file=None, + ssl_passwd=None) -> SSLContextContainer: """Convenience method for easy creation of SSL context, assuming all needed files are in the same folder. Create an ssl context from files 'userkey.pem', 'usercert.pem', and optional 'cacert.pem' and cyphers file :param ca_folder: base path of all files :param private_key: name of the private key file of the user :param certificate: name of the signed certificate of the user :param ca_public_key: name of public key of the certificate authority that signed the certificate; if given, - verify_mode of ssl_context will be set to CERT_REQUIRED + verify_mode of ssl contexts in the return value will be set to CERT_REQUIRED :param cyphers_file: optional file that contains a cyphers string; comments are possible, start line with '#' :param ssl_passwd: optional password string - :return: SSLContext instance + :return: container of SSLContext instances i.e. client_ssl_context and server_ssl_context """ certfile = os.path.join(ca_folder, certificate) if not os.path.exists(certfile): @@ -49,29 +56,40 @@ def mk_ssl_context_from_folder(ca_folder, break else: cyphers = None - return mk_ssl_context(keyfile, certfile, cafile, cyphers, ssl_passwd) + return mk_ssl_contexts(keyfile, certfile, cafile, cyphers, ssl_passwd) -def mk_ssl_context(key_file, - cert_file, - ca_file, - cyphers=None, - ssl_passwd=None) -> ssl.SSLContext: +def mk_ssl_contexts(key_file, + cert_file, + ca_file, + cyphers=None, + ssl_passwd=None) -> SSLContextContainer: """Convenience method for easy creation of SSL context. Create an ssl context from files 'userkey.pem', 'usercert.pem', 'cacert.pem' and optional 'cyphers.json' :param key_file: the private key pem file of the user :param cert_file: the signed certificate of the user :param ca_file: optional public key of the certificate authority that signed the certificate; if given, - verify_mode of ssl_context will be set to CERT_REQUIRED + verify_mode of ssl contexts in the return value will be set to CERT_REQUIRED :param cyphers: optional cyphers string :param ssl_passwd: optional password string - :return: SSLContext instance + :return: container of SSLContext instances i.e. client_ssl_context and server_ssl_context """ - ssl_context = ssl.SSLContext() # defaults to ssl.PROTOCOL_TLS - ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=ssl_passwd) + client_ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + server_ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + + client_ssl_context.check_hostname = False + client_ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=ssl_passwd) if cyphers is not None: - ssl_context.set_ciphers(cyphers) + client_ssl_context.set_ciphers(cyphers) if ca_file: - ssl_context.verify_mode = ssl.CERT_REQUIRED - ssl_context.load_verify_locations(ca_file) - return ssl_context + client_ssl_context.verify_mode = ssl.CERT_REQUIRED + client_ssl_context.load_verify_locations(ca_file) + + server_ssl_context.load_cert_chain(certfile=cert_file, keyfile=key_file, password=ssl_passwd) + if cyphers is not None: + server_ssl_context.set_ciphers(cyphers) + if ca_file: + server_ssl_context.verify_mode = ssl.CERT_REQUIRED + server_ssl_context.load_verify_locations(ca_file) + + return SSLContextContainer(client_context=client_ssl_context, server_context=server_ssl_context) diff --git a/src/sdc11073/consumer/consumerimpl.py b/src/sdc11073/consumer/consumerimpl.py index 7f86e39a..fad12fb2 100644 --- a/src/sdc11073/consumer/consumerimpl.py +++ b/src/sdc11073/consumer/consumerimpl.py @@ -12,6 +12,7 @@ from lxml import etree as etree_ +import sdc11073.certloader from sdc11073 import commlog, loghelper, network from sdc11073 import observableproperties as properties from sdc11073.definitions_base import ProtocolsRegistry @@ -47,7 +48,6 @@ from .subscription import ConsumerSubscription - class HostedServiceDescription: """HostedServiceDescription collects initial structural data from provider.""" @@ -63,7 +63,7 @@ def __init__(self, service_id: str, # noqa: PLR0913 self.msg_factory = msg_factory self._data_model = data_model self.log_prefix = log_prefix - self.meta_data: mex_types.Metadata| None = None + self.meta_data: mex_types.Metadata | None = None self.wsdl_string = None self.wsdl_node: etree_.ElementBase | None = None self._logger = loghelper.get_logger_adapter('sdc.client.hosted', log_prefix) @@ -185,7 +185,7 @@ class SdcConsumer: def __init__(self, device_location: str, # noqa: PLR0913 sdc_definitions: type[BaseDefinitions], - ssl_context: SSLContext | None, + ssl_context_container: sdc11073.certloader.SSLContextContainer | None, epr: str | uuid.UUID | None = None, validate: bool = True, log_prefix: str = '', @@ -198,7 +198,7 @@ def __init__(self, device_location: str, # noqa: PLR0913 :param device_location: the XAddr location for meta data, e.g. http://10.52.219.67:62616/72c08f50-74cc-11e0-8092-027599143341 :param sdc_definitions: a class derived from BaseDefinitions :param epr: the path of this client in http server - :param ssl_context: used for ssl connection to device and for own HTTP Server (notifications receiver) + :param ssl_context_container: used for ssl connection to device and for own HTTP Server (notifications receiver) :param validate: bool :param log_prefix: a string used as prefix for logging :param specific_components: a SdcConsumerComponents instance or None @@ -231,7 +231,7 @@ def __init__(self, device_location: str, # noqa: PLR0913 self._logger.info('Using SSL is enabled. TLS 1.3 Support = {}', ssl.HAS_TLSv1_3) # noqa: PLE1205 except AttributeError: self._logger.info('Using SSL is enabled. TLS 1.3 is not supported') - self._ssl_context = ssl_context + self._ssl_context_container = ssl_context_container self._epr = epr or uuid.uuid4() self._http_server = None @@ -260,9 +260,9 @@ def __init__(self, device_location: str, # noqa: PLR0913 msg_factory_cls = self._components.msg_factory_class self.msg_factory = msg_factory_cls(self.sdc_definitions, - additional_schema_specs, - self._logger, - validate=validate) + additional_schema_specs, + self._logger, + validate=validate) action_dispatcher_class = self._components.action_dispatcher_class self._services_dispatcher = action_dispatcher_class(log_prefix) @@ -419,7 +419,7 @@ def localization_service_client(self) -> HostedServiceClient: return self.client('LocalizationService') @property - def subscription_mgr(self)-> ConsumerSubscriptionManagerProtocol: + def subscription_mgr(self) -> ConsumerSubscriptionManagerProtocol: """Return the subscription manager.""" return self._subscription_mgr @@ -512,7 +512,7 @@ def start_all(self, not_subscribed_actions: list[str] | None = None, # noqa: PL filter_type.Dialect = DeviceEventingFilterDialectURI.ACTION try: self.do_subscribe(dpws_hosted, filter_type, subscribe_actions) - except Exception: # noqa: BLE001 + except Exception: # noqa: BLE001 self.all_subscribed = False # => don't log errors when mdib versions are missing self._logger.error('start_all: could not subscribe: error = {}, actions= {}', # noqa: PLE1205 traceback.format_exc(), subscribe_actions) @@ -530,7 +530,7 @@ def set_is_connected(is_ok: bool): properties.strongbind(self._subscription_mgr, all_subscriptions_okay=set_is_connected) self.is_connected = self._subscription_mgr.all_subscriptions_okay - def stop_all(self, unsubscribe: bool =True): + def stop_all(self, unsubscribe: bool = True): """Stop all threads, optionally unsubscribe.""" if self._subscription_mgr is not None: if unsubscribe: @@ -552,7 +552,7 @@ def _get_metadata(self) -> mex_types.Metadata: _url = urlparse(self._device_location) wsc = self.get_soap_client(self._device_location) - if self._ssl_context is not None and _url.scheme == 'https': + if self._ssl_context_container is not None and _url.scheme == 'https': if wsc.is_closed(): wsc.connect() sock = wsc.sock @@ -595,7 +595,8 @@ def get_soap_client(self, address: str) -> SoapClientProtocol: def _mk_soap_client(self, scheme: str, # noqa: PLR0913 netloc: str) -> SoapClientProtocol: - _ssl_context = self._ssl_context if scheme == "https" else None + _ssl_context = \ + self._ssl_context_container.client_context if scheme == "https" and self._ssl_context_container else None cls = self._components.soap_client_class return cls(netloc, self._socket_timeout, @@ -636,11 +637,11 @@ def _mk_hosted_service_client(self, port_type: str, def _start_event_sink(self, shared_http_server: Any): if shared_http_server is None: self._is_internal_http_server = True - ssl_context = self._ssl_context if self._device_uses_https else None + ssl_context_container = self._ssl_context_container if self._device_uses_https else None logger = loghelper.get_logger_adapter('sdc.client.notif_dispatch', self.log_prefix) self._http_server = HttpServerThreadBase( str(self._network_adapter.ip), - ssl_context, + ssl_context_container.server_context if ssl_context_container else None, logger=logger, supported_encodings=self._compression_methods, ) @@ -669,8 +670,8 @@ def __str__(self) -> str: return f'SdcConsumer to {self.host_description.this_device} {self.host_description.this_model} on {self._device_location}' @classmethod - def from_wsd_service(cls, wsd_service: Service, # noqa: PLR0913 - ssl_context: SSLContext | None, + def from_wsd_service(cls, wsd_service: Service, # noqa: PLR0913 + ssl_context_container: sdc11073.certloader.SSLContextContainer | None, validate: bool = True, log_prefix: str = '', default_components: SdcConsumerComponents | None = None, @@ -678,7 +679,7 @@ def from_wsd_service(cls, wsd_service: Service, # noqa: PLR0913 """Construct a SdcConsumer from a Service. :param wsd_service: a wsdiscovery.Service instance - :param ssl_context: a ssl context or None + :param ssl_context_container: a ssl context or None :param validate: bool :param log_prefix: a string :param default_components: a SdcConsumerComponents instance or None @@ -691,6 +692,7 @@ def from_wsd_service(cls, wsd_service: Service, # noqa: PLR0913 device_location = device_locations[0] for sdc_definition in ProtocolsRegistry.protocols: if sdc_definition.types_match(wsd_service.types): - return cls(device_location, sdc_definition, ssl_context, validate=validate, log_prefix=log_prefix, + return cls(device_location, sdc_definition, ssl_context_container, validate=validate, + log_prefix=log_prefix, default_components=default_components, specific_components=specific_components) raise RuntimeError('no matching protocol definition found for this service!') diff --git a/src/sdc11073/httpserver/httpserverimpl.py b/src/sdc11073/httpserver/httpserverimpl.py index 9ede2ca3..24a23c14 100644 --- a/src/sdc11073/httpserver/httpserverimpl.py +++ b/src/sdc11073/httpserver/httpserverimpl.py @@ -122,7 +122,7 @@ def run(self): self.my_port = self.httpd.server_port self.logger.info('starting http server on {}:{}', self._my_ipaddress, self.my_port) if self._ssl_context: - self.httpd.socket = self._ssl_context.wrap_socket(self.httpd.socket) + self.httpd.socket = self._ssl_context.wrap_socket(self.httpd.socket, server_side=True) self.base_url = f'https://{self._my_ipaddress}:{self.my_port}/' else: self.base_url = f'http://{self._my_ipaddress}:{self.my_port}/' diff --git a/src/sdc11073/provider/providerimpl.py b/src/sdc11073/provider/providerimpl.py index 214cbcac..5a853969 100644 --- a/src/sdc11073/provider/providerimpl.py +++ b/src/sdc11073/provider/providerimpl.py @@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any, Protocol from urllib.parse import SplitResult +import sdc11073.certloader from sdc11073 import loghelper from sdc11073 import observableproperties as properties from sdc11073.dispatch import ( @@ -84,7 +85,7 @@ def __init__(self, ws_discovery: WsDiscoveryProtocol, device_mdib_container: ProviderMdib, epr: str | uuid.UUID | None = None, validate: bool = True, - ssl_context: SSLContext | None = None, + ssl_context_container: sdc11073.certloader.SSLContextContainer | None = None, max_subscription_duration: int = 15, socket_timeout: int | float | None = None, log_prefix: str = '', @@ -100,7 +101,7 @@ def __init__(self, ws_discovery: WsDiscoveryProtocol, :param epr: something that serves as a unique identifier of this device for discovery. If epr is a string, it must be usable as a path element in an url (no spaces, ...) :param validate: bool - :param ssl_context: if not None, this context is used and https url is used, otherwise http + :param ssl_context_container: if not None, the contexts are used and an https url is used, otherwise http :param max_subscription_duration: max. possible duration of a subscription :param socket_timeout: timeout for tcp sockets that send notifications. If None, it is set to max_subscription_duration * 1.2 @@ -117,7 +118,7 @@ def __init__(self, ws_discovery: WsDiscoveryProtocol, else: self._epr = epr self._validate = validate - self._ssl_context = ssl_context + self._ssl_context_container = ssl_context_container self._max_subscription_duration = max_subscription_duration self._socket_timeout = socket_timeout or int(max_subscription_duration * 1.2) self._log_prefix = log_prefix @@ -136,7 +137,7 @@ def __init__(self, ws_discovery: WsDiscoveryProtocol, self._http_server = None self._is_internal_http_server = False - if self._ssl_context is not None: + if self._ssl_context_container is not None: self._urlschema = 'https' else: self._urlschema = 'http' @@ -182,7 +183,7 @@ def __init__(self, ws_discovery: WsDiscoveryProtocol, self._msg_converter = MessageConverterMiddleware( self.msg_reader, self.msg_factory, self._logger, self._hosted_service_dispatcher) - self._transaction_id = 0 # central transaction number handling for all called operations. + self._transaction_id = 0 # central transaction number handling for all called operations. self._transaction_id_lock = threading.Lock() # these are initialized in _setup_components: @@ -209,7 +210,7 @@ def _mk_soap_client(self, netloc: str, accepted_encodings: list[str]) -> Any: return cls(netloc, self._socket_timeout, loghelper.get_logger_adapter('sdc.device.soap', self._log_prefix), - ssl_context=self._ssl_context, + ssl_context=self._ssl_context_container.client_context if self._ssl_context_container else None, sdc_definitions=self._mdib.sdc_definitions, msg_reader=self.msg_reader, supported_encodings=self._compression_methods, @@ -408,8 +409,11 @@ def _start_services(self, shared_http_server=None): logger = loghelper.get_logger_adapter('sdc.device.httpsrv', self._log_prefix) self._http_server = HttpServerThreadBase( - my_ipaddress='0.0.0.0', ssl_context=self._ssl_context, supported_encodings=self._compression_methods, - logger=logger, chunk_size=self.chunk_size) + my_ipaddress='0.0.0.0', + ssl_context=self._ssl_context_container.server_context if self._ssl_context_container else None, + supported_encodings=self._compression_methods, + logger=logger, + chunk_size=self.chunk_size) # first start http server, the services need to know the ip port number self._http_server.start() diff --git a/tests/mockstuff.py b/tests/mockstuff.py index bfb499cc..cd257b36 100644 --- a/tests/mockstuff.py +++ b/tests/mockstuff.py @@ -9,6 +9,7 @@ from lxml import etree as etree_ +import sdc11073.certloader from sdc11073.mdib import ProviderMdib from sdc11073.namespaces import default_ns_helper as ns_hlp from sdc11073.provider import SdcProvider @@ -116,7 +117,7 @@ def __init__(self, wsdiscovery: WsDiscoveryProtocol, mdib_xml_data: bytes, epr: str | uuid.UUID | None = None, validate: bool = True, - ssl_context: SSLContext | None = None, + ssl_context_container: sdc11073.certloader.SSLContextContainer | None = None, max_subscription_duration: int = 15, log_prefix: str = '', default_components: SdcProviderComponents | None = None, @@ -142,7 +143,7 @@ def __init__(self, wsdiscovery: WsDiscoveryProtocol, mdsDescriptor.MetaData.SerialNumber.append('ABCD-1234') mdsDescriptor.MetaData.ModelNumber = '0.99' super().__init__(wsdiscovery, model, device, device_mdib_container, epr, validate, - ssl_context=ssl_context, + ssl_context_container=ssl_context_container, max_subscription_duration = max_subscription_duration, log_prefix=log_prefix, default_components=default_components, @@ -155,7 +156,7 @@ def from_mdib_file(cls, epr: str | uuid.UUID | None, mdib_xml_path: str, validate: bool =True, - ssl_context: SSLContext | None = None, + ssl_context_container: sdc11073.certloader.SSLContextContainer | None = None, max_subscription_duration: int = 15, log_prefix: str = '', default_components: SdcProviderComponents | None = None, @@ -168,7 +169,7 @@ def from_mdib_file(cls, with open(mdib_xml_path, 'rb') as f: mdib_xml_data = f.read() - return cls(wsdiscovery, mdib_xml_data, epr, validate, ssl_context, + return cls(wsdiscovery, mdib_xml_data, epr, validate, ssl_context_container, max_subscription_duration = max_subscription_duration, log_prefix=log_prefix, default_components=default_components, specific_components=specific_components, diff --git a/tests/test_alertsignaldelegate.py b/tests/test_alertsignaldelegate.py index 61048323..7748f1a1 100644 --- a/tests/test_alertsignaldelegate.py +++ b/tests/test_alertsignaldelegate.py @@ -49,7 +49,7 @@ def setUp(self): xAddr = self.sdc_device.get_xaddrs() self.sdc_client = SdcConsumer(xAddr[0], sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, + ssl_context_container=None, validate=CLIENT_VALIDATE, log_prefix=' ') self.sdc_client.start_all() diff --git a/tests/test_client_device.py b/tests/test_client_device.py index b85c1bc1..051e275d 100644 --- a/tests/test_client_device.py +++ b/tests/test_client_device.py @@ -1,19 +1,26 @@ import copy import datetime import logging +import socket +import ssl import sys import time import traceback import unittest +import unittest.mock from decimal import Decimal from itertools import product from lxml import etree as etree_ +import sdc11073.certloader +import sdc11073.definitions_sdc from sdc11073 import commlog from sdc11073 import loghelper from sdc11073 import observableproperties -from sdc11073.xml_types import pm_types, msg_qnames as msg, pm_qnames as pm +from sdc11073.consumer import SdcConsumer +from sdc11073.consumer.components import SdcConsumerComponents +from sdc11073.consumer.subscription import ClientSubscriptionManagerReferenceParams from sdc11073.dispatch import RequestDispatcher from sdc11073.httpserver import compression from sdc11073.httpserver.httpserverimpl import HttpServerThreadBase @@ -21,10 +28,16 @@ from sdc11073.loghelper import basic_logging_setup, get_logger_adapter from sdc11073.mdib import ConsumerMdib from sdc11073.mdib.providerwaveform import Annotator +from sdc11073.namespaces import default_ns_helper +from sdc11073.provider import waveforms +from sdc11073.provider.components import SdcProviderComponents, default_sdc_provider_components_async +from sdc11073.provider.subscriptionmgr_async import SubscriptionsManagerReferenceParamAsync +from sdc11073.pysoap.msgfactory import CreatedMessage from sdc11073.pysoap.soapclient import HTTPReturnCodeError from sdc11073.pysoap.soapclient_async import SoapClientAsync from sdc11073.pysoap.soapenvelope import Soap12Envelope, faultcodeEnum -from sdc11073.pysoap.msgfactory import CreatedMessage +from sdc11073.wsdiscovery import WSDiscovery +from sdc11073.xml_types import pm_types, msg_qnames as msg, pm_qnames as pm from sdc11073.xml_types.addressing_types import HeaderInformationBlock from sdc11073.consumer import SdcConsumer from sdc11073.consumer.components import SdcConsumerComponents @@ -94,13 +107,14 @@ def runtest_basic_connect(unit_test, sdc_client): get_result = context_service.get_context_states() unit_test.assertGreater(len(get_result.result.ContextState), 0) + def runtest_directed_probe(unit_test, sdc_client, sdc_device): probe_matches = sdc_client.send_probe() unit_test.assertEqual(1, len(probe_matches.ProbeMatch)) probe_match = probe_matches.ProbeMatch[0] unit_test.assertEqual(1, len(probe_match.XAddrs)) unit_test.assertEqual(probe_match.XAddrs[0], sdc_device.get_xaddrs()[0]) - print (probe_matches) + print(probe_matches) def runtest_realtime_samples(unit_test, sdc_device, sdc_client): @@ -231,6 +245,197 @@ def runtest_metric_reports(unit_test, sdc_device, sdc_client, logger, test_perio unit_test.assertGreaterEqual(len(report.ReportPart), 1) +class ClientDeviceSSLIntegration(unittest.TestCase): + """ + Integration test for the sdc11073 client and sdc11073 device regarding their usage of ssl context objects. + """ + + @staticmethod + def wrap_socket(self, sock, *args, **kwargs): + + def accept(self, *args, **kwargs): + conn, address = self.old_accept(*args, **kwargs) + + sock.branches.append(conn) + + return conn, address + + new_socket = self.old_wrap_socket(sock.s, *args, **kwargs) + new_socket.old_accept = new_socket.accept + new_socket.accept = accept.__get__(new_socket, socket.SocketType) + + m = unittest.mock.Mock(wraps=new_socket) + sock.w.append(m) + + return m + + def test_basic_connection_with_different_ssl_contexts(self): + """ + Test that client and server contexts are used only for their intended purpose. + """ + client_ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + server_ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + + client_ssl_context.check_hostname = False + + client_ssl_context.verify_mode = ssl.CERT_NONE + server_ssl_context.verify_mode = ssl.CERT_NONE + + # this is intentionally unsafe so that the unittest is simplified to work without dh params and rsa keys + client_ssl_context.set_ciphers('ALL:@SECLEVEL=0') + server_ssl_context.set_ciphers('ALL:@SECLEVEL=0') + + client_ssl_context_wrap_socket_mock = unittest.mock.Mock( + side_effect=self.wrap_socket.__get__(client_ssl_context, ssl.SSLContext)) + server_ssl_context_wrap_socket_mock = unittest.mock.Mock( + side_effect=self.wrap_socket.__get__(server_ssl_context, ssl.SSLContext)) + + client_ssl_context.old_wrap_socket = client_ssl_context.wrap_socket + client_ssl_context.wrap_socket = client_ssl_context_wrap_socket_mock + server_ssl_context.old_wrap_socket = server_ssl_context.wrap_socket + server_ssl_context.wrap_socket = server_ssl_context_wrap_socket_mock + + ssl_context_container = sdc11073.certloader.SSLContextContainer(client_context=client_ssl_context, + server_context=server_ssl_context) + + original_socket_socket = socket.socket + + def socket_init_side_effect(*args, **kwargs): + + s = original_socket_socket(*args, **kwargs) + m = unittest.mock.Mock(wraps=s) + + m.s = s + m.w = list() + + m.branches = list() + + m.family = s.family + + return m + + class SocketSocketMock(unittest.mock.Mock): + + def __instancecheck__(self, instance): + return original_socket_socket.__instancecheck__(instance) + + socket_socket_mock = SocketSocketMock(side_effect=socket_init_side_effect) + + with unittest.mock.patch.object(socket, 'socket', new=socket_socket_mock): + + self._run_client_with_device(ssl_context_container) + + socket_socket_mock.assert_called() + + self.assertGreaterEqual(len(client_ssl_context_wrap_socket_mock.call_args_list), 1) + + for call_arg in client_ssl_context_wrap_socket_mock.call_args_list: + if call_arg[0]: + # TODO: replace call_arg[0] with call_arg.args when Python 3.7 support is dropped + sock = call_arg[0][0] + else: + # TODO: replace call_arg[1] with call_arg.kwargs when Python 3.7 support is dropped + sock = call_arg[1]['sock'] + + self.assertIn(unittest.mock.call.connect(unittest.mock.ANY), sock.method_calls) + self.assertNotIn(unittest.mock.call.listen(unittest.mock.ANY), sock.method_calls) + self.assertNotIn(unittest.mock.call.listen(), sock.method_calls) + + self.assertGreaterEqual(len(server_ssl_context_wrap_socket_mock.call_args_list), 1) + + branches = list() + + for call_arg in server_ssl_context_wrap_socket_mock.call_args_list: + if call_arg[0]: + sock = call_arg[0][0] + else: + sock = call_arg[1]['sock'] + + branches.extend(sock.branches) + + for call_arg in server_ssl_context_wrap_socket_mock.call_args_list: + if call_arg[0]: + sock = call_arg[0][0] + else: + sock = call_arg[1]['sock'] + + self.assertNotIn(unittest.mock.call.connect(unittest.mock.ANY), sock.method_calls) + self.assertTrue(unittest.mock.call.listen(unittest.mock.ANY) in sock.method_calls or + unittest.mock.call.listen() in sock.method_calls or set(sock.w).intersection(branches)) + + @unittest.mock.patch('os.path.exists') + def test_mk_ssl_contexts(self, _): + """ + Test that sdc11073.certloader.mk_ssl_contexts_from_folder creates different contexts for client and device. + """ + original_ssl_context = ssl.SSLContext + + ssl_context_mock_list: list[unittest.mock.Mock] = list() + + def ssl_context_init_side_effect(*args, **kwargs): + s = original_ssl_context(*args, **kwargs) + m = unittest.mock.Mock(wraps=s) + + m.load_cert_chain = unittest.mock.MagicMock() + m.load_verify_locations = unittest.mock.MagicMock() + + ssl_context_mock_list.append(m) + return m + + ssl_context_mock = unittest.mock.Mock(side_effect=ssl_context_init_side_effect) + + with unittest.mock.patch.object(ssl, 'SSLContext', new=ssl_context_mock): + return_value = sdc11073.certloader.mk_ssl_contexts_from_folder('') + + self.assertNotEqual(return_value.client_context, return_value.server_context) + + ssl_context_mock.assert_called() + ssl_context_mock.assert_any_call(ssl.PROTOCOL_TLS_CLIENT) + ssl_context_mock.assert_any_call(ssl.PROTOCOL_TLS_SERVER) + + self.assertGreaterEqual(len(ssl_context_mock_list), 2) + + for context_mock in ssl_context_mock_list: + context_mock.load_cert_chain.assert_called() + context_mock.load_verify_locations.assert_called() + + @staticmethod + def _run_client_with_device(ssl_context_container): + log_watcher = loghelper.LogWatcher(logging.getLogger('sdc'), level=logging.ERROR) + basic_logging_setup() + wsd = WSDiscovery('127.0.0.1') + wsd.start() + location = SdcLocation(fac='fac1', poc='CU1', bed='Bed') + sdc_device = SomeDevice.from_mdib_file(wsd, None, mdib_70041, ssl_context_container=ssl_context_container) + sdc_device.start_all(periodic_reports_interval=1.0) + _loc_validators = [pm_types.InstanceIdentifier('Validator', extension_string='System')] + sdc_device.set_location(location, _loc_validators) + provide_realtime_data(sdc_device) + + time.sleep(0.5) + specific_components = SdcConsumerComponents( + action_dispatcher_class=RequestDispatcher + ) + + x_addr = sdc_device.get_xaddrs() + sdc_client = SdcConsumer(x_addr[0], + sdc_definitions=sdc_device.mdib.sdc_definitions, + ssl_context_container=ssl_context_container, + validate=CLIENT_VALIDATE, + specific_components=specific_components) + sdc_client.start_all(subscribe_periodic_reports=True) + time.sleep(1.5) + + log_watcher.setPaused(True) + if sdc_device: + sdc_device.stop_all() + if sdc_client: + sdc_client.stop_all() + wsd.stop() + + log_watcher.check() + + class Test_Client_SomeDevice(unittest.TestCase): def setUp(self): basic_logging_setup() @@ -256,10 +461,10 @@ def setUp(self): x_addr = self.sdc_device.get_xaddrs() self.sdc_client = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, - validate=CLIENT_VALIDATE, - specific_components=specific_components) + sdc_definitions=self.sdc_device.mdib.sdc_definitions, + ssl_context_container=None, + validate=CLIENT_VALIDATE, + specific_components=specific_components) self.sdc_client.start_all(subscribe_periodic_reports=True) time.sleep(1) sys.stderr.write('\n############### setUp done {} ##############\n'.format(self._testMethodName)) @@ -289,7 +494,6 @@ def test_basic_connect(self): runtest_basic_connect(self, self.sdc_client) runtest_directed_probe(self, self.sdc_client, self.sdc_device) - def test_renew_get_status(self): for s in self.sdc_client._subscription_mgr.subscriptions.values(): max_duration = self.sdc_device._max_subscription_duration @@ -298,7 +502,7 @@ def test_renew_get_status(self): remaining_seconds = s.get_status() self.assertAlmostEqual(remaining_seconds, max_duration, delta=5.0) # huge diff allowed due to jenkins # verify that device returns fault message on wrong subscription identifier - #if s.dev_reference_param.has_parameters: + # if s.dev_reference_param.has_parameters: if s.subscribe_response.SubscriptionManager.ReferenceParameters: # ToDo: manipulate reference parameter pass @@ -496,10 +700,10 @@ def test_instance_id(self): action_dispatcher_class=RequestDispatcher ) sdc_client = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, - validate=CLIENT_VALIDATE, - specific_components=specific_components, + sdc_definitions=self.sdc_device.mdib.sdc_definitions, + ssl_context_container=None, + validate=CLIENT_VALIDATE, + specific_components=specific_components, log_prefix='consumer2 ') sdc_client.start_all(subscribe_periodic_reports=True) @@ -533,7 +737,6 @@ def test_roundtrip_times(self): self.assertTrue(stats.min >= 0) self.assertTrue(found) - def test_alert_reports(self): """ verify that the client receives correct EpisodicAlertReports and PeriodicAlertReports""" client_mdib = ConsumerMdib(self.sdc_client) @@ -642,7 +845,7 @@ def test_set_patient_context_on_device(self): pm.PatientContextState, allow_none=True) self.assertEqual(patient_context_state_container.CoreData.Givenname, 'Moritz') self.assertGreater(patient_context_state_container.BindingMdibVersion, - tr_MdibVersion) + tr_MdibVersion) self.assertEqual(patient_context_state_container.UnbindingMdibVersion, None) def test_get_containment_tree(self): @@ -660,7 +863,7 @@ def test_get_supported_languages(self): storage.add( pm_types.LocalizedText('bla', lang='de-de', ref='a', version=1, text_width=pm_types.LocalizedTextWidth.XS), pm_types.LocalizedText('foo', lang='en-en', ref='a', version=1, text_width=pm_types.LocalizedTextWidth.XS) - ) + ) get_request_response = self.sdc_client.localization_service_client.get_supported_languages() languages = get_request_response.result.Lang @@ -671,21 +874,21 @@ def test_get_supported_languages(self): def test_get_localized_texts(self): storage = self.sdc_device.localization_storage storage.add(pm_types.LocalizedText('bla_a', lang='de-de', ref='a', version=1, - text_width=pm_types.LocalizedTextWidth.XS)) + text_width=pm_types.LocalizedTextWidth.XS)) storage.add(pm_types.LocalizedText('foo_a', lang='en-en', ref='a', version=1, - text_width=pm_types.LocalizedTextWidth.XS)) + text_width=pm_types.LocalizedTextWidth.XS)) storage.add(pm_types.LocalizedText('bla_b', lang='de-de', ref='b', version=1, - text_width=pm_types.LocalizedTextWidth.XS)) + text_width=pm_types.LocalizedTextWidth.XS)) storage.add(pm_types.LocalizedText('foo_b', lang='en-en', ref='b', version=1, - text_width=pm_types.LocalizedTextWidth.XS)) + text_width=pm_types.LocalizedTextWidth.XS)) storage.add(pm_types.LocalizedText('bla_aa', lang='de-de', ref='a', version=2, - text_width=pm_types.LocalizedTextWidth.S)) + text_width=pm_types.LocalizedTextWidth.S)) storage.add(pm_types.LocalizedText('foo_aa', lang='en-en', ref='a', version=2, - text_width=pm_types.LocalizedTextWidth.S)) + text_width=pm_types.LocalizedTextWidth.S)) storage.add(pm_types.LocalizedText('bla_bb', lang='de-de', ref='b', version=2, - text_width=pm_types.LocalizedTextWidth.S)) + text_width=pm_types.LocalizedTextWidth.S)) storage.add(pm_types.LocalizedText('foo_bb', lang='en-en', ref='b', version=2, - text_width=pm_types.LocalizedTextWidth.S)) + text_width=pm_types.LocalizedTextWidth.S)) service_client = self.sdc_client.localization_service_client get_request_response = service_client.get_localized_texts() report = get_request_response.result @@ -1117,20 +1320,20 @@ def setUp(self): x_addr = self.sdc_device_1.get_xaddrs() self.sdc_client_1 = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device_1.mdib.sdc_definitions, - ssl_context=None, - epr="client1", - validate=CLIENT_VALIDATE, - log_prefix=' ') + sdc_definitions=self.sdc_device_1.mdib.sdc_definitions, + ssl_context_container=None, + epr="client1", + validate=CLIENT_VALIDATE, + log_prefix=' ') self.sdc_client_1.start_all(shared_http_server=self.httpserver) x_addr = self.sdc_device_2.get_xaddrs() self.sdc_client_2 = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device_2.mdib.sdc_definitions, - ssl_context=None, - epr="client2", - validate=CLIENT_VALIDATE, - log_prefix=' ') + sdc_definitions=self.sdc_device_2.mdib.sdc_definitions, + ssl_context_container=None, + epr="client2", + validate=CLIENT_VALIDATE, + log_prefix=' ') self.sdc_client_2.start_all(shared_http_server=self.httpserver) self._all_cl_dev = ((self.sdc_client_1, self.sdc_device_1), @@ -1194,11 +1397,11 @@ def setUp(self): x_addr = self.sdc_device.get_xaddrs() self.sdc_client = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, - validate=CLIENT_VALIDATE, - log_prefix=' ', - request_chunk_size=512) + sdc_definitions=self.sdc_device.mdib.sdc_definitions, + ssl_context_container=None, + validate=CLIENT_VALIDATE, + log_prefix=' ', + request_chunk_size=512) self.sdc_client.start_all() time.sleep(1) @@ -1249,12 +1452,12 @@ def setUp(self): x_addr = self.sdc_device.get_xaddrs() specific_components = SdcConsumerComponents(subscription_manager_class=ClientSubscriptionManagerReferenceParams) self.sdc_client = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, - validate=CLIENT_VALIDATE, - log_prefix=' ', - specific_components=specific_components, - request_chunk_size=512) + sdc_definitions=self.sdc_device.mdib.sdc_definitions, + ssl_context_container=None, + validate=CLIENT_VALIDATE, + log_prefix=' ', + specific_components=specific_components, + request_chunk_size=512) self.sdc_client.start_all() time.sleep(1) @@ -1335,11 +1538,11 @@ def setUp(self): x_addr = self.sdc_device.get_xaddrs() self.sdc_client = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, - validate=CLIENT_VALIDATE, - log_prefix='', - request_chunk_size=512) + sdc_definitions=self.sdc_device.mdib.sdc_definitions, + ssl_context_container=None, + validate=CLIENT_VALIDATE, + log_prefix='', + request_chunk_size=512) self.sdc_client.start_all(subscribe_periodic_reports=True) time.sleep(1) diff --git a/tests/test_client_waveform.py b/tests/test_client_waveform.py index 8e0d2b28..8ab9233c 100644 --- a/tests/test_client_waveform.py +++ b/tests/test_client_waveform.py @@ -105,7 +105,7 @@ def setUp(self): self.log_watcher = loghelper.LogWatcher(logging.getLogger('sdc'), level=logging.ERROR) self.sdc_client = SdcConsumer(DEV_ADDRESS, sdc_definitions=definitions_sdc.SDC_v1_Definitions, - ssl_context=None, + ssl_context_container=None, validate=CLIENT_VALIDATE) def tearDown(self): diff --git a/tests/test_compression.py b/tests/test_compression.py index afc32f0d..a50f9491 100644 --- a/tests/test_compression.py +++ b/tests/test_compression.py @@ -63,7 +63,7 @@ def _start_with_compression(self, compression_flag): x_addr = self.sdc_device.get_xaddrs() self.sdc_client = SdcConsumer(x_addr[0], sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, + ssl_context_container=None, ) if compression_flag is None: self.sdc_client.set_used_compression() diff --git a/tests/test_device_periodic_reports.py b/tests/test_device_periodic_reports.py index d296b84a..a02a0a5e 100644 --- a/tests/test_device_periodic_reports.py +++ b/tests/test_device_periodic_reports.py @@ -46,7 +46,7 @@ def setUp(self): x_addr = self.sdc_device.get_xaddrs() self.sdc_client = SdcConsumer(x_addr[0], sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, + ssl_context_container=None, validate=CLIENT_VALIDATE) print('############### setUp done {} ##############'.format(self._testMethodName)) diff --git a/tests/test_operations.py b/tests/test_operations.py index a5c665ae..c88c0d9e 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -79,10 +79,10 @@ def setUp(self): action_dispatcher_class=RequestDispatcher ) self.sdc_client = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, - validate=CLIENT_VALIDATE, - specific_components=specific_components) + sdc_definitions=self.sdc_device.mdib.sdc_definitions, + ssl_context_container=None, + validate=CLIENT_VALIDATE, + specific_components=specific_components) self.sdc_client.start_all(subscribe_periodic_reports=True) time.sleep(1) self._logger.info('############### setUp done %s ##############', self._testMethodName) @@ -172,7 +172,7 @@ def test_set_patient_context_operation(self): # test update of the patient proposed_context = context.mk_proposed_context_object(patient_descriptor_container.Handle, - handle=patient_context_state_container.Handle) + handle=patient_context_state_container.Handle) proposed_context.CoreData.Givenname = 'Karla' future = context.set_context_state(operation_handle, [proposed_context]) result = future.result(timeout=SET_TIMEOUT) @@ -344,10 +344,10 @@ def test_audio_pause_two_clients(self): action_dispatcher_class=RequestDispatcher ) sdc_client2 = SdcConsumer(x_addr[0], - sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, - validate=CLIENT_VALIDATE, - specific_components=specific_components, + sdc_definitions=self.sdc_device.mdib.sdc_definitions, + ssl_context_container=None, + validate=CLIENT_VALIDATE, + specific_components=specific_components, log_prefix='client2') sdc_client2.start_all(subscribe_periodic_reports=True) try: @@ -477,7 +477,8 @@ def test_set_metric_state(self): operation_handle = my_operation_descriptor.Handle proposed_metric_state = clientMdib.xtra.mk_proposed_state(operation_target_handle) - self.assertIsNone(proposed_metric_state.LifeTimePeriod) # just to be sure that we know the correct intitial value + self.assertIsNone( + proposed_metric_state.LifeTimePeriod) # just to be sure that we know the correct intitial value before_state_version = proposed_metric_state.StateVersion newLifeTimePeriod = 42.5 proposed_metric_state.LifeTimePeriod = newLifeTimePeriod @@ -522,7 +523,7 @@ def test_set_component_state(self): new_operating_hours = 42 proposed_component_state.OperatingHours = new_operating_hours future = set_service.set_component_state(operation_handle=operation_handle, - proposed_component_states=[proposed_component_state]) + proposed_component_states=[proposed_component_state]) result = future.result(timeout=SET_TIMEOUT) state = result.InvocationInfo.InvocationState self.assertEqual(state, msg_types.InvocationState.FINISHED) @@ -549,4 +550,3 @@ def test_operation_without_handler(self): future2 = set_service.set_string(operation_handle=operation_handle, requested_string=value) result2 = future2.result(timeout=SET_TIMEOUT) self.assertGreater(result2.InvocationInfo.TransactionId, result.InvocationInfo.TransactionId) - diff --git a/tests/test_string_enum_descriptors.py b/tests/test_string_enum_descriptors.py index dd4f8c0d..135b386c 100644 --- a/tests/test_string_enum_descriptors.py +++ b/tests/test_string_enum_descriptors.py @@ -48,7 +48,7 @@ def setUp(self): x_addr = self.sdc_device.get_xaddrs() self.sdc_client = SdcConsumer(x_addr[0], sdc_definitions=self.sdc_device.mdib.sdc_definitions, - ssl_context=None, + ssl_context_container=None, validate=CLIENT_VALIDATE) self.sdc_client.start_all() diff --git a/tests/test_tutorial.py b/tests/test_tutorial.py index 412002ec..549ce91b 100644 --- a/tests/test_tutorial.py +++ b/tests/test_tutorial.py @@ -271,7 +271,7 @@ def test_createClient(self): scopes=ScopesType(self.my_location.scope_string)) self.assertEqual(len(services), 1) # both devices found - my_client = SdcConsumer.from_wsd_service(services[0], ssl_context=None) + my_client = SdcConsumer.from_wsd_service(services[0], ssl_context_container=None) self.my_clients.append(my_client) my_client.start_all() ############# Mdib usage ############################## @@ -313,7 +313,7 @@ def test_call_operation(self): scopes=ScopesType(self.my_location.scope_string)) self.assertEqual(len(services), 1) # both devices found - my_client = SdcConsumer.from_wsd_service(services[0], ssl_context=None) + my_client = SdcConsumer.from_wsd_service(services[0], ssl_context_container=None) self.my_clients.append(my_client) my_client.start_all() my_mdib = ConsumerMdib(my_client) @@ -369,7 +369,7 @@ def test_operation_handler(self): scopes=ScopesType(self.my_location.scope_string)) self.assertEqual(len(services), 1) - self.service = SdcConsumer.from_wsd_service(services[0], ssl_context=None) + self.service = SdcConsumer.from_wsd_service(services[0], ssl_context_container=None) my_client = self.service self.my_clients.append(my_client) my_client.start_all()