holado 0.2.7__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of holado might be problematic. Click here for more details.
- holado/common/handlers/undefined.py +7 -1
- {holado-0.2.7.dist-info → holado-0.3.0.dist-info}/METADATA +7 -1
- {holado-0.2.7.dist-info → holado-0.3.0.dist-info}/RECORD +116 -107
- holado_ais/ais/ais_messages.py +97 -6
- holado_ais/tests/behave/steps/ais/ais_manager_steps.py +1 -1
- holado_ais/tests/behave/steps/ais/ais_messages_steps.py +45 -6
- holado_binary/ipc/bit_series.py +3 -3
- holado_binary/tests/behave/steps/ipc/binary_steps.py +1 -1
- holado_binary/tests/behave/steps/ipc/bit_series_steps.py +4 -3
- holado_context/tests/behave/steps/private/common/context_steps.py +1 -1
- holado_core/common/resource/persisted_data_manager.py +13 -16
- holado_core/common/resource/resource_manager.py +10 -10
- holado_core/common/tables/converters/table_converter.py +47 -9
- holado_core/common/tables/table.py +2 -2
- holado_core/common/tables/table_manager.py +6 -7
- holado_core/common/tables/table_with_header.py +6 -0
- holado_core/common/tools/string_tools.py +9 -0
- holado_core/tests/behave/steps/common/common_steps.py +2 -1
- holado_core/tests/behave/steps/common/config_steps.py +1 -1
- holado_core/tests/behave/steps/common/resource_steps.py +1 -1
- holado_core/tests/behave/steps/common/tables_steps.py +27 -4
- holado_data/data/generator/generator_manager.py +39 -0
- holado_data/tests/behave/steps/data/generator_steps.py +1 -1
- holado_data/tests/behave/steps/tools/utils_steps.py +1 -2
- holado_db/tests/behave/steps/tools/db/db_client_steps.py +1 -1
- holado_db/tests/behave/steps/tools/db/postgresql_client_steps.py +1 -1
- holado_db/tests/behave/steps/tools/db/sqlite_client_steps.py +1 -1
- holado_db/tools/db/clients/base/db_client.py +81 -28
- holado_db/tools/db/clients/postgresql/postgresql_client.py +17 -7
- holado_db/tools/db/query/base/query_builder.py +58 -7
- holado_db/tools/db/query/pypika/pypika_query_builder.py +73 -21
- holado_docker/tests/behave/steps/tools/docker_steps.py +1 -1
- holado_grpc/tests/behave/steps/api/grpc_client_steps.py +1 -1
- holado_grpc/tests/behave/steps/private/api/grpc_steps.py +1 -1
- holado_helper/script/action.py +16 -7
- holado_json/tests/behave/steps/ipc/json_steps.py +1 -1
- holado_keycloak/tests/behave/steps/tools/keycloak_client_steps.py +1 -1
- holado_multitask/tests/behave/steps/multiprocessing_steps.py +1 -1
- holado_multitask/tests/behave/steps/multithreading_steps.py +1 -1
- holado_protobuf/ipc/protobuf/types/google/protobuf.py +1 -1
- holado_protobuf/tests/behave/steps/ipc/protobuf_steps.py +1 -1
- holado_python/common/tools/datetime.py +31 -12
- holado_python/standard_library/socket/blocking_socket.py +112 -42
- holado_python/standard_library/socket/echo_server.py +4 -3
- holado_python/standard_library/socket/message_socket.py +69 -22
- holado_python/standard_library/socket/non_blocking_socket.py +65 -67
- holado_python/standard_library/socket/socket.py +272 -32
- holado_python/standard_library/ssl/resources/certificates/NOTES.txt +1 -1
- holado_python/standard_library/ssl/resources/certificates/rootCACert.pem +24 -0
- holado_python/standard_library/ssl/resources/certificates/tcpbin.crt +21 -0
- holado_python/standard_library/ssl/resources/certificates/tcpbin.key +28 -0
- holado_python/standard_library/ssl/ssl.py +138 -21
- holado_python/tests/behave/steps/convert_steps.py +1 -1
- holado_python/tests/behave/steps/iterable_steps.py +1 -1
- holado_python/tests/behave/steps/standard_library/csv_steps.py +1 -1
- holado_python/tests/behave/steps/standard_library/datetime_steps.py +1 -1
- holado_python/tests/behave/steps/standard_library/hashlib_steps.py +1 -1
- holado_python/tests/behave/steps/standard_library/multiprocessing_steps.py +1 -1
- holado_python/tests/behave/steps/standard_library/queue_steps.py +1 -1
- holado_python/tests/behave/steps/standard_library/socket_steps.py +147 -21
- holado_python/tests/behave/steps/standard_library/ssl_steps.py +87 -16
- holado_rabbitmq/tests/behave/steps/tools/rabbitmq_client_steps.py +48 -20
- holado_rabbitmq/tests/behave/steps/tools/rabbitmq_server_steps.py +1 -1
- holado_rabbitmq/tools/rabbitmq/rabbitmq_client.py +19 -13
- holado_rabbitmq/tools/rabbitmq/rabbitmq_manager.py +2 -29
- holado_redis/tests/behave/steps/tools/redis_client_steps.py +1 -1
- holado_rest/tests/behave/steps/api/rest_client_steps.py +1 -1
- holado_rest/tests/behave/steps/private/api/rest_steps.py +1 -1
- holado_s3/tests/behave/steps/private/tools/s3_steps.py +1 -1
- holado_s3/tests/behave/steps/tools/s3_client_steps.py +1 -1
- holado_s3/tests/behave/steps/tools/s3_server_steps.py +1 -1
- holado_scripting/tests/behave/steps/common/tools/variable_convert_steps.py +3 -2
- holado_scripting/tests/behave/steps/common/tools/variable_new_steps.py +1 -1
- holado_scripting/tests/behave/steps/common/tools/variable_steps.py +1 -1
- holado_scripting/tests/behave/steps/common/tools/variable_verify_steps.py +1 -1
- holado_scripting/tests/behave/steps/scenario/function_steps.py +1 -1
- holado_scripting/tests/behave/steps/scenario/if_steps.py +1 -1
- holado_scripting/tests/behave/steps/scenario/loop_steps.py +1 -1
- holado_scripting/text/interpreter/functions/function_apply_function.py +60 -0
- holado_scripting/text/interpreter/functions/function_to_string.py +50 -0
- holado_scripting/text/interpreter/text_interpreter.py +4 -0
- holado_sftp/tests/behave/steps/private/tools/sftp_steps.py +1 -1
- holado_sftp/tests/behave/steps/tools/sftp_client_steps.py +1 -1
- holado_sftp/tests/behave/steps/tools/sftp_server_steps.py +1 -1
- holado_swagger/tests/behave/steps/swagger_hub/mockserver_steps.py +1 -1
- holado_system/system/command/command.py +14 -9
- holado_system/tests/behave/steps/system/commands_steps.py +1 -1
- holado_system/tests/behave/steps/system/file_steps.py +1 -1
- holado_system/tests/behave/steps/system/system_steps.py +1 -1
- holado_test/scenario/step_tools.py +5 -4
- holado_test/scenario/tester_tools.py +6 -3
- holado_test/tests/behave/steps/scenario/exception_steps.py +1 -1
- holado_test/tests/behave/steps/scenario/scenario_steps.py +1 -1
- holado_test/tests/behave/steps/scenario/tester_steps.py +4 -4
- holado_value/common/tables/converters/value_table_converter.py +52 -8
- holado_value/common/tables/value_table_cell.py +5 -0
- holado_value/common/tables/value_table_manager.py +0 -10
- holado_value/common/tables/value_table_row.py +0 -1
- holado_value/common/tools/value.py +5 -1
- holado_ws/tests/behave/steps/api/web_service_steps.py +1 -1
- holado_yaml/tests/behave/steps/yaml_steps.py +1 -1
- holado_yaml/yaml/yaml_manager.py +2 -2
- test_holado/features/NonReg/common/tables/table.feature +30 -24
- test_holado/features/NonReg/holado_ais/ais_message-bitarray_to_nmea.feature +1 -1
- test_holado/features/NonReg/holado_python/standard_library/socket/local_echo_server/socket_reset.feature +191 -0
- test_holado/features/NonReg/holado_python/standard_library/{socket_with_ssl.feature → socket/local_echo_server/socket_with_tls_and_verify.feature} +126 -27
- test_holado/features/NonReg/holado_python/standard_library/socket/local_echo_server/socket_with_tls_without_verify.feature +299 -0
- test_holado/features/NonReg/holado_python/standard_library/{socket.feature → socket/local_echo_server/socket_without_tls.feature} +63 -1
- test_holado/features/NonReg/holado_python/standard_library/socket/tcpbin.com/socket_with_mtls.feature +214 -0
- test_holado/features/NonReg/holado_python/standard_library/socket/tcpbin.com/socket_with_tls.feature +184 -0
- test_holado/features/NonReg/holado_python/standard_library/socket/tcpbin.com/socket_without_tls.feature +169 -0
- test_holado/features/NonReg/tools/RabbitMQ.feature +9 -9
- test_holado/features/NonReg/tools/RabbitMQ_steps.feature +8 -8
- test_holado/logging.conf +5 -3
- holado_core/common/transport/__init__.py +0 -0
- holado_core/common/transport/crc.py +0 -40
- {holado-0.2.7.dist-info → holado-0.3.0.dist-info}/WHEEL +0 -0
- {holado-0.2.7.dist-info → holado-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -13,12 +13,20 @@
|
|
|
13
13
|
|
|
14
14
|
import logging
|
|
15
15
|
import socket
|
|
16
|
-
from _socket import SHUT_RDWR
|
|
16
|
+
from _socket import SHUT_RDWR, SOCK_STREAM, error
|
|
17
17
|
from holado.common.handlers.object import DeleteableObject
|
|
18
18
|
import abc
|
|
19
19
|
from holado_core.common.exceptions.functional_exception import FunctionalException
|
|
20
20
|
from holado_core.common.tools.tools import Tools
|
|
21
21
|
from holado_python.standard_library.ssl.ssl import SslManager
|
|
22
|
+
import threading
|
|
23
|
+
import types
|
|
24
|
+
from abc import abstractmethod
|
|
25
|
+
from holado.common.handlers.undefined import undefined_argument
|
|
26
|
+
import copy
|
|
27
|
+
from holado_python.standard_library.typing import Typing
|
|
28
|
+
import ssl
|
|
29
|
+
import select
|
|
22
30
|
|
|
23
31
|
logger = logging.getLogger(__name__)
|
|
24
32
|
|
|
@@ -31,14 +39,83 @@ class Socket(DeleteableObject):
|
|
|
31
39
|
"""
|
|
32
40
|
__metaclass__ = abc.ABCMeta
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
@classmethod
|
|
43
|
+
def create(cls, name, internal_socket):
|
|
44
|
+
res = cls.__new__(cls, name=name)
|
|
45
|
+
res.__init__(name=name)
|
|
46
|
+
res._set_internal_socket(internal_socket)
|
|
47
|
+
return res
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def create_connection(cls, address, timeout=undefined_argument,
|
|
51
|
+
source_address=None, do_connect=True):
|
|
52
|
+
"""Connect to *address* and return the socket object.
|
|
53
|
+
|
|
54
|
+
Convenience function. Connect to *address* (a 2-tuple ``(host,
|
|
55
|
+
port)``) and return the socket object. Passing the optional
|
|
56
|
+
*timeout* parameter will set the timeout on the socket instance
|
|
57
|
+
before attempting to connect. If no *timeout* is supplied, the
|
|
58
|
+
global default timeout setting returned by :func:`getdefaulttimeout`
|
|
59
|
+
is used. If *source_address* is set it must be a tuple of (host, port)
|
|
60
|
+
for the socket to bind as a source address before making the connection.
|
|
61
|
+
A host of '' or port 0 tells the OS to use the default.
|
|
62
|
+
|
|
63
|
+
Note: Implementation is a copy of socket.create_connection, but allowing to not connect.
|
|
64
|
+
If do_connect==True, implementation is identical to socket.create_connection.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
host, port = address
|
|
68
|
+
err = None
|
|
69
|
+
for res in socket.getaddrinfo(host, port, 0, SOCK_STREAM):
|
|
70
|
+
af, socktype, proto, canonname, sa = res # @UnusedVariable
|
|
71
|
+
sock = None
|
|
72
|
+
try:
|
|
73
|
+
sock = socket.socket(af, socktype, proto)
|
|
74
|
+
if timeout is not undefined_argument:
|
|
75
|
+
sock.settimeout(timeout)
|
|
76
|
+
if source_address:
|
|
77
|
+
sock.bind(source_address)
|
|
78
|
+
if do_connect:
|
|
79
|
+
sock.connect(sa)
|
|
80
|
+
# Break explicitly a reference cycle
|
|
81
|
+
err = None
|
|
82
|
+
return sock
|
|
83
|
+
|
|
84
|
+
except error as _:
|
|
85
|
+
err = _
|
|
86
|
+
if sock is not None:
|
|
87
|
+
sock.close()
|
|
88
|
+
|
|
89
|
+
if err is not None:
|
|
90
|
+
try:
|
|
91
|
+
raise err
|
|
92
|
+
finally:
|
|
93
|
+
# Break explicitly a reference cycle
|
|
94
|
+
err = None
|
|
95
|
+
else:
|
|
96
|
+
raise error("getaddrinfo returns an empty list")
|
|
97
|
+
|
|
98
|
+
def __init__(self, *, name=None, create_ipv4_socket_kwargs=None, idle_sleep_delay=undefined_argument):
|
|
99
|
+
"""Socket constructor
|
|
100
|
+
@param name: Socket name
|
|
101
|
+
@param create_ipv4_socket_kwargs: arguments to create an IPv4 socket
|
|
102
|
+
@param idle_sleep_delay: delay to sleep when socket is idle (used in some clients/servers to control CPU resource impact ; default: 0.01 s)
|
|
103
|
+
"""
|
|
35
104
|
if name is None and create_ipv4_socket_kwargs is not None:
|
|
36
105
|
if not set(create_ipv4_socket_kwargs.keys()).issuperset({'host', 'port'}):
|
|
37
106
|
raise FunctionalException(f"Parameters 'host' and 'port' must be defined")
|
|
38
|
-
name = f"{create_ipv4_socket_kwargs['host']}:{create_ipv4_socket_kwargs['port']}"
|
|
107
|
+
name = f"{Typing.get_object_class_name(self)}({create_ipv4_socket_kwargs['host']}:{create_ipv4_socket_kwargs['port']})"
|
|
39
108
|
|
|
40
109
|
super().__init__(name)
|
|
41
110
|
self.__socket = None
|
|
111
|
+
self.__is_started = False
|
|
112
|
+
self.__start_thread = None
|
|
113
|
+
self.__idle_sleep_delay = idle_sleep_delay if idle_sleep_delay is not undefined_argument else 0.01
|
|
114
|
+
|
|
115
|
+
# SSL management
|
|
116
|
+
self.__ssl_kwargs_tuple = None
|
|
117
|
+
self.__ssl_context = None
|
|
118
|
+
self.__is_ssl_handshake_done = False
|
|
42
119
|
|
|
43
120
|
if create_ipv4_socket_kwargs is not None:
|
|
44
121
|
self.create_ipv4_socket(**create_ipv4_socket_kwargs)
|
|
@@ -56,8 +133,37 @@ class Socket(DeleteableObject):
|
|
|
56
133
|
def internal_socket(self) -> socket.socket:
|
|
57
134
|
return self.__socket
|
|
58
135
|
|
|
59
|
-
|
|
136
|
+
@property
|
|
137
|
+
def is_started(self):
|
|
138
|
+
return self.__is_started
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def is_with_ssl(self):
|
|
142
|
+
return self.__ssl_context is not None
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def _ssl_kwargs(self):
|
|
146
|
+
return copy.copy(self.__ssl_kwargs_tuple[0]) if self.__ssl_kwargs_tuple and self.__ssl_kwargs_tuple[0] is not None else None
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def _ssl_context_kwargs(self):
|
|
150
|
+
return copy.copy(self.__ssl_kwargs_tuple[1]) if self.__ssl_kwargs_tuple and self.__ssl_kwargs_tuple[1] is not None else None
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def _ssl_wrap_socket_kwargs(self):
|
|
154
|
+
return copy.copy(self.__ssl_kwargs_tuple[2]) if self.__ssl_kwargs_tuple and self.__ssl_kwargs_tuple[2] is not None else None
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def _ssl_context(self):
|
|
158
|
+
return self.__ssl_context
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def _idle_sleep_delay(self):
|
|
162
|
+
return self.__idle_sleep_delay
|
|
163
|
+
|
|
164
|
+
def _set_internal_socket(self, socket, is_ssl_handshake_done_on_connect=True):
|
|
60
165
|
self.__socket = socket
|
|
166
|
+
self.__is_ssl_handshake_done = is_ssl_handshake_done_on_connect
|
|
61
167
|
|
|
62
168
|
@abc.abstractmethod
|
|
63
169
|
def create_ipv4_socket(self, host, port, **kwargs):
|
|
@@ -68,15 +174,43 @@ class Socket(DeleteableObject):
|
|
|
68
174
|
try:
|
|
69
175
|
self.__socket.shutdown(SHUT_RDWR)
|
|
70
176
|
except OSError as exc:
|
|
71
|
-
if 'Errno
|
|
72
|
-
logger.info(f"Got following error on socket shutdown (known to appear
|
|
177
|
+
if 'Errno 9' in str(exc):
|
|
178
|
+
logger.info(f"Got following error on socket shutdown (known to appear when connection is not open correctly or already closed): {exc}")
|
|
179
|
+
elif 'Errno 107' in str(exc):
|
|
180
|
+
logger.info(f"Got following error on socket shutdown (known to appear sometimes during shutdown): {exc}")
|
|
73
181
|
else:
|
|
74
182
|
raise exc
|
|
75
|
-
|
|
76
|
-
|
|
183
|
+
finally:
|
|
184
|
+
self.__socket.close()
|
|
185
|
+
self.__socket = None
|
|
186
|
+
|
|
187
|
+
@abstractmethod
|
|
188
|
+
def start(self, *, read_bufsize=1024, read_kwargs=None, write_kwargs=None):
|
|
189
|
+
raise NotImplementedError()
|
|
190
|
+
|
|
191
|
+
def _start_thread(self, thread):
|
|
192
|
+
# Start thread
|
|
193
|
+
self.__start_thread = thread
|
|
194
|
+
self.__start_thread.start()
|
|
195
|
+
self.__is_started = True
|
|
196
|
+
|
|
197
|
+
if Tools.do_log(logger, logging.DEBUG):
|
|
198
|
+
logger.debug(f"[{self.name}] Started")
|
|
199
|
+
|
|
200
|
+
def stop(self):
|
|
201
|
+
if self.__start_thread is not None:
|
|
202
|
+
if self.__start_thread.is_interruptable:
|
|
203
|
+
self.__start_thread.interrupt()
|
|
204
|
+
self.__start_thread.join()
|
|
205
|
+
self.__start_thread = None
|
|
206
|
+
self.__is_started = False
|
|
207
|
+
|
|
208
|
+
if Tools.do_log(logger, logging.DEBUG):
|
|
209
|
+
logger.debug(f"[{self.name}] Stopped")
|
|
77
210
|
|
|
78
211
|
def read(self, bufsize=1024, **kwargs):
|
|
79
|
-
|
|
212
|
+
flags = kwargs.get('flags', 0)
|
|
213
|
+
res = self.internal_socket.recv(bufsize, flags)
|
|
80
214
|
if logger.isEnabledFor(logging.DEBUG):
|
|
81
215
|
logger.debug(f"[{self.name}] Received [{res}] (type: {type(res)})")
|
|
82
216
|
return res
|
|
@@ -93,14 +227,50 @@ class Socket(DeleteableObject):
|
|
|
93
227
|
logger.debug(f"[{self.name}] Sent {res}/{len(data_bytes)} bytes of [{data_bytes}] (type: {type(data_bytes)})")
|
|
94
228
|
return res
|
|
95
229
|
|
|
96
|
-
def
|
|
97
|
-
"""
|
|
230
|
+
def _extract_ssl_kwargs(self, socket_kwargs):
|
|
231
|
+
"""Pop from socket kwargs: global ssl kwargs, SSLContext kwargs, and wrap_socket kwargs.
|
|
98
232
|
"""
|
|
99
|
-
|
|
233
|
+
ssl_kwargs, context_kwargs, wrap_socket_kwargs = None, {}, {}
|
|
100
234
|
if Tools.has_sub_kwargs(socket_kwargs, 'ssl.'):
|
|
101
235
|
ssl_kwargs = Tools.pop_sub_kwargs(socket_kwargs, 'ssl.')
|
|
102
|
-
|
|
103
|
-
|
|
236
|
+
if Tools.has_sub_kwargs(ssl_kwargs, 'context.'):
|
|
237
|
+
context_kwargs = Tools.pop_sub_kwargs(ssl_kwargs, 'context.')
|
|
238
|
+
if Tools.has_sub_kwargs(ssl_kwargs, 'wrap_socket.'):
|
|
239
|
+
wrap_socket_kwargs = Tools.pop_sub_kwargs(ssl_kwargs, 'wrap_socket.')
|
|
240
|
+
self.__ssl_kwargs_tuple = (ssl_kwargs, context_kwargs, wrap_socket_kwargs)
|
|
241
|
+
return socket_kwargs
|
|
242
|
+
|
|
243
|
+
def _new_ssl_context_if_required(self, server_side=False, **kwargs):
|
|
244
|
+
"""Return the remaining socket kwargs after ssl kwargs extraction.
|
|
245
|
+
"""
|
|
246
|
+
res = self._extract_ssl_kwargs(kwargs)
|
|
247
|
+
|
|
248
|
+
if self._ssl_kwargs is not None:
|
|
249
|
+
self.__ssl_context = SslManager.new_ssl_context(server_side=server_side, ssl_kwargs=self._ssl_kwargs, context_kwargs=self._ssl_context_kwargs)
|
|
250
|
+
|
|
251
|
+
return res
|
|
252
|
+
|
|
253
|
+
def do_ssl_handshake(self):
|
|
254
|
+
if self.is_with_ssl:
|
|
255
|
+
if self.internal_socket.getblocking():
|
|
256
|
+
self.internal_socket.do_handshake()
|
|
257
|
+
# self.internal_socket.do_handshake(block=True)
|
|
258
|
+
else:
|
|
259
|
+
while True:
|
|
260
|
+
try:
|
|
261
|
+
self.internal_socket.do_handshake()
|
|
262
|
+
break
|
|
263
|
+
except ssl.SSLWantReadError:
|
|
264
|
+
select.select([self.internal_socket], [], [])
|
|
265
|
+
except ssl.SSLWantWriteError:
|
|
266
|
+
select.select([], [self.internal_socket], [])
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def ensure_ssl_handshake_is_done(self):
|
|
271
|
+
if self.is_with_ssl and not self.__is_ssl_handshake_done:
|
|
272
|
+
self.do_ssl_handshake()
|
|
273
|
+
self.__is_ssl_handshake_done = True
|
|
104
274
|
|
|
105
275
|
|
|
106
276
|
class SocketClient(Socket):
|
|
@@ -109,10 +279,91 @@ class SocketClient(Socket):
|
|
|
109
279
|
"""
|
|
110
280
|
__metaclass__ = abc.ABCMeta
|
|
111
281
|
|
|
112
|
-
def __init__(self, *, name=None, create_ipv4_socket_kwargs=None):
|
|
113
|
-
|
|
282
|
+
def __init__(self, *, name=None, create_ipv4_socket_kwargs=None, idle_sleep_delay=undefined_argument, do_run_with_recv=True, do_run_with_send=True):
|
|
283
|
+
"""Socket client constructor
|
|
284
|
+
@param name: Socket client name
|
|
285
|
+
@param create_ipv4_socket_kwargs: Arguments to create an IPv4 socket
|
|
286
|
+
@param idle_sleep_delay: delay to sleep when socket is idle (used only in some clients to control CPU resource impact ; default: 0.01 s)
|
|
287
|
+
@param do_run_with_recv: Define if recv is done in run process when client is started
|
|
288
|
+
@param do_run_with_send: Define if send is done in run process when client is started
|
|
289
|
+
"""
|
|
290
|
+
# Data used in start processing
|
|
291
|
+
self.__data_lock = threading.Lock()
|
|
292
|
+
self.__data = types.SimpleNamespace(
|
|
293
|
+
in_bytes = b'',
|
|
294
|
+
out_bytes = b'',
|
|
295
|
+
)
|
|
296
|
+
self.__with_recv = do_run_with_recv
|
|
297
|
+
self.__with_send = do_run_with_send
|
|
298
|
+
|
|
299
|
+
# Note: super().__init__ must be done after self.__data & others initialization since it can execute create_ipv4_socket_kwargs method
|
|
300
|
+
super().__init__(name=name, create_ipv4_socket_kwargs=create_ipv4_socket_kwargs, idle_sleep_delay=idle_sleep_delay)
|
|
114
301
|
|
|
115
|
-
|
|
302
|
+
@property
|
|
303
|
+
def is_run_with_recv(self):
|
|
304
|
+
return self.__with_recv
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def is_run_with_send(self):
|
|
308
|
+
return self.__with_send
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def _data_lock(self):
|
|
312
|
+
return self.__data_lock
|
|
313
|
+
|
|
314
|
+
@property
|
|
315
|
+
def _data(self):
|
|
316
|
+
return self.__data
|
|
317
|
+
|
|
318
|
+
def _delete_object(self):
|
|
319
|
+
self.stop()
|
|
320
|
+
super()._delete_object()
|
|
321
|
+
|
|
322
|
+
def _start_thread(self, thread):
|
|
323
|
+
# Ensure SSL handshake is done before starting to receive and send data
|
|
324
|
+
self.ensure_ssl_handshake_is_done()
|
|
325
|
+
|
|
326
|
+
# Start thread
|
|
327
|
+
super()._start_thread(thread)
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def received_data(self):
|
|
331
|
+
with self.__data_lock:
|
|
332
|
+
res = copy.copy(self.__data.in_bytes)
|
|
333
|
+
return res
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def received_data_size(self):
|
|
337
|
+
with self.__data_lock:
|
|
338
|
+
res = len(self.__data.in_bytes)
|
|
339
|
+
return res
|
|
340
|
+
|
|
341
|
+
def reset_received_data(self):
|
|
342
|
+
with self.__data_lock:
|
|
343
|
+
self.__data.in_bytes = b''
|
|
344
|
+
|
|
345
|
+
def read(self, bufsize=1024, **kwargs):
|
|
346
|
+
if self.is_started and self.is_run_with_recv:
|
|
347
|
+
with self.__data_lock:
|
|
348
|
+
if self.__data.in_bytes:
|
|
349
|
+
res = self.__data.in_bytes[:bufsize]
|
|
350
|
+
self.__data.in_bytes = self.__data.in_bytes[bufsize:]
|
|
351
|
+
else:
|
|
352
|
+
res = b''
|
|
353
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
354
|
+
logger.debug(f"[{self.name}] Read data from received data: [{res}] (type: {type(res)} ; remaining data: {len(self.__data.in_bytes)})")
|
|
355
|
+
return res
|
|
356
|
+
else:
|
|
357
|
+
return super().read(bufsize=bufsize, **kwargs)
|
|
358
|
+
|
|
359
|
+
def write(self, data_bytes, loop_until_all_is_sent=True, **kwargs):
|
|
360
|
+
if self.is_started and self.is_run_with_send:
|
|
361
|
+
with self.__data_lock:
|
|
362
|
+
self.__data.out_bytes += data_bytes
|
|
363
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
364
|
+
logger.debug(f"[{self.name}] Added data in data to send (total: {len(self.__data.out_bytes)})")
|
|
365
|
+
else:
|
|
366
|
+
return super().write(data_bytes, loop_until_all_is_sent=loop_until_all_is_sent, **kwargs)
|
|
116
367
|
|
|
117
368
|
|
|
118
369
|
class SocketServer(Socket):
|
|
@@ -121,27 +372,16 @@ class SocketServer(Socket):
|
|
|
121
372
|
"""
|
|
122
373
|
__metaclass__ = abc.ABCMeta
|
|
123
374
|
|
|
124
|
-
def __init__(self, *, name=None, create_ipv4_socket_kwargs=None):
|
|
125
|
-
super().__init__(name=name, create_ipv4_socket_kwargs=create_ipv4_socket_kwargs)
|
|
375
|
+
def __init__(self, *, name=None, create_ipv4_socket_kwargs=None, idle_sleep_delay=undefined_argument):
|
|
376
|
+
super().__init__(name=name, create_ipv4_socket_kwargs=create_ipv4_socket_kwargs, idle_sleep_delay=idle_sleep_delay)
|
|
126
377
|
|
|
127
378
|
def accept(self):
|
|
128
379
|
conn, addr = self.internal_socket.accept()
|
|
129
|
-
res = Socket(name=f"[{self.name}] Connection with {addr}")
|
|
130
|
-
res._set_internal_socket(conn)
|
|
380
|
+
res = Socket.create(name=f"[{self.name}] Connection with {addr}", internal_socket=conn)
|
|
131
381
|
return res, addr
|
|
132
382
|
|
|
133
383
|
@abc.abstractmethod
|
|
134
|
-
def
|
|
135
|
-
"""Start server.
|
|
136
|
-
"""
|
|
137
|
-
raise NotImplementedError()
|
|
138
|
-
|
|
139
|
-
@abc.abstractmethod
|
|
140
|
-
def stop(self):
|
|
141
|
-
raise NotImplementedError()
|
|
142
|
-
|
|
143
|
-
@abc.abstractmethod
|
|
144
|
-
def _process_data(self, data):
|
|
384
|
+
def _process_received_data(self, data):
|
|
145
385
|
raise NotImplementedError()
|
|
146
386
|
|
|
147
387
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
localhost.
|
|
1
|
+
localhost.crt and localhost.key were generated with command:
|
|
2
2
|
openssl req -x509 -out localhost.crt -keyout localhost.key \
|
|
3
3
|
-newkey rsa:2048 -nodes -sha256 \
|
|
4
4
|
-subj '/CN=localhost' -extensions EXT -config <( \
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIID7jCCAtagAwIBAgIJAJNpPsX/1B+fMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
|
|
3
|
+
VQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDzAN
|
|
4
|
+
BgNVBAoMBnRjcGJpbjEMMAoGA1UECwwDb3BzMRMwEQYDVQQDDAp0Y3BiaW4uY29t
|
|
5
|
+
MSMwIQYJKoZIhvcNAQkBFhRoYXJyeWJhZ2RpQGdtYWlsLmNvbTAeFw0xOTA1MDUw
|
|
6
|
+
NjI3NTlaFw0yOTA1MDIwNjI3NTlaMIGLMQswCQYDVQQGEwJVUzELMAkGA1UECAwC
|
|
7
|
+
Q0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDzANBgNVBAoMBnRjcGJpbjEMMAoG
|
|
8
|
+
A1UECwwDb3BzMRMwEQYDVQQDDAp0Y3BiaW4uY29tMSMwIQYJKoZIhvcNAQkBFhRo
|
|
9
|
+
YXJyeWJhZ2RpQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
|
10
|
+
ggEBAK0LTwoX+/d/QKJuIuvUw2gUBGwjeX8DM3OqagcmZutlq9VfSW1pkzNhCpFU
|
|
11
|
+
1JQh/bJ3P97VTZn75H2zL4BQnAXaPSRoi0wOFsoEhzbIOAFELPq2TMoY9lvOLO9n
|
|
12
|
+
4yt4Pz7AbFLhPDeltqb91S/18hN1YmVTDEuMCz4OGnJOf5NbmuVwmjMcDWiOwmjp
|
|
13
|
+
dBYlwCMn/TRB3wUPSwafLef9EIJfm4uL0ruH3Rlhj8Lktt0YTvrA7M6URMcgFqYG
|
|
14
|
+
wNNsYa9X88Js1nxlryvTputbVf6zL35S7CvTGc6fft/pfdSAu2KcmOd+C0dew1GI
|
|
15
|
+
daM9tqTiqi6Tg+IA2t4eaA3tR2MCAwEAAaNTMFEwHQYDVR0OBBYEFOLuMowBSAZf
|
|
16
|
+
V5v82LmlaIIOvU/DMB8GA1UdIwQYMBaAFOLuMowBSAZfV5v82LmlaIIOvU/DMA8G
|
|
17
|
+
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEj3o18u8FQDrQu89Etk
|
|
18
|
+
4sQgiZxtQ0C3OglCXP9kRGSBt8Ymd/2bmpGK2TsEpACkfpJLaFRVPek2302YQ8mc
|
|
19
|
+
oj63wPTDYx1P/BSH+dPvDUXI9SePXcrFAPq9YemteQHE+WZ/PFe0wAq1ehziWgqQ
|
|
20
|
+
OiMb1wydDbaRZF6jPPMW7cHCiUYceLBRO0ZTe9CsUPYw6A78QZnO6P7Cuc1SwkNv
|
|
21
|
+
uejCGTfv5HP72/Jm24ifAMOoWXALmvIIAnOVTwpPEX2nEnr9H1S8Psi3+mqkGyC1
|
|
22
|
+
RQXprzHkflCKHxjiNfNguHeC3x6l0LGWEShcemyDWCsTgCYNX6RMTWWcug3Vb/70
|
|
23
|
+
BSs=
|
|
24
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDZTCCAk2gAwIBAgIBKjANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMCVVMx
|
|
3
|
+
CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQKDAZ0
|
|
4
|
+
Y3BiaW4xDDAKBgNVBAsMA29wczETMBEGA1UEAwwKdGNwYmluLmNvbTEjMCEGCSqG
|
|
5
|
+
SIb3DQEJARYUaGFycnliYWdkaUBnbWFpbC5jb20wHhcNMjUwNTI4MTMxODIwWhcN
|
|
6
|
+
MjUwNTI5MTMxODIwWjAcMRowGAYDVQQDDBF0Y3BiaW4uY29tLWNsaWVudDCCASIw
|
|
7
|
+
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANvo9G/86es6qoCHTum0RAoqAAiY
|
|
8
|
+
qnOCLgXmamgfQgbfYoBIkNTBBVzsZyoZIY4QuuO8E07VmbrnH23czCSAO5akPb7D
|
|
9
|
+
teotATqDf4My0Ybht62nLoxhHBb9jrzSgv4+04gy/W6wu5diyfeY8BNopHkUkmEN
|
|
10
|
+
WhmGkP9s9npUGaEfEeasuiB1JMkAEOWnw+kHqE3Us+4Av4Mht6srswst3B90WCIV
|
|
11
|
+
Rry48gRJS4qZoAk1dwZHIcYSOfMJ8Pu69wS1qjRwRPLc3dfnUs2E3dAx1FDxDBkK
|
|
12
|
+
eQ73VUm64A5AXLYRk3PvgPwbcgerkLAnYAb5o8YJmyufGXKQNhmZxcCt+t0CAwEA
|
|
13
|
+
AaNCMEAwHQYDVR0OBBYEFDgjXXI6juiInalFSZEZM6BcEW9UMB8GA1UdIwQYMBaA
|
|
14
|
+
FOLuMowBSAZfV5v82LmlaIIOvU/DMA0GCSqGSIb3DQEBCwUAA4IBAQAu6SJ3OT6D
|
|
15
|
+
fhjfg4wRMHZVsaYSF6Kwe0RNt6izRkv5MUGi6Ew0SK1Mm+qxE51G3aaVCE4G+P1e
|
|
16
|
+
ACOiBL55saWDJtdng2VTKhUsFxIk2c/t+tuh7ttV7i0yGbaLGDP0N2FveBhKj/5d
|
|
17
|
+
SaPpAQvZ1rFhXpUU1jQr+xi5pP/iOPH02O8A8VgHyGebDVXs8iWMHjozOrRhgQVJ
|
|
18
|
+
YAl5lyYs+aj199p9ixwY3RPPWbUcggvAipv9bu6uEEIWMOMMERM2P7yFYaWJBcw5
|
|
19
|
+
A/Uqu2bBLmOu9/qmlEj2uQfTeVhnoT/bZBfmPcrL64tHjw7G5u0WuooPNTI23z28
|
|
20
|
+
3ttcr+fDbDR4
|
|
21
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
|
2
|
+
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDb6PRv/OnrOqqA
|
|
3
|
+
h07ptEQKKgAImKpzgi4F5mpoH0IG32KASJDUwQVc7GcqGSGOELrjvBNO1Zm65x9t
|
|
4
|
+
3MwkgDuWpD2+w7XqLQE6g3+DMtGG4betpy6MYRwW/Y680oL+PtOIMv1usLuXYsn3
|
|
5
|
+
mPATaKR5FJJhDVoZhpD/bPZ6VBmhHxHmrLogdSTJABDlp8PpB6hN1LPuAL+DIber
|
|
6
|
+
K7MLLdwfdFgiFUa8uPIESUuKmaAJNXcGRyHGEjnzCfD7uvcEtao0cETy3N3X51LN
|
|
7
|
+
hN3QMdRQ8QwZCnkO91VJuuAOQFy2EZNz74D8G3IHq5CwJ2AG+aPGCZsrnxlykDYZ
|
|
8
|
+
mcXArfrdAgMBAAECggEAe76UfcfloTY752M8ZonHl6iWqD+v+puQZkWILtsX/mIJ
|
|
9
|
+
PYKX7QBIkkd8rdXCafzEDY4xlzTe8qtHpjyOqyN1ZIk7LXNXlFSK0nBYem1INgwh
|
|
10
|
+
nZfru5aRheZcQah7ibG0unlm3riYdtFiMO9geKtzkaafz/kBcEemo/SepatZWK3m
|
|
11
|
+
DQX6okkpHh04GZNIO52K2QCaYpmj14fXIi2p5M4+KD+gmYbXhkmzNdcjz2l/wkqo
|
|
12
|
+
SziZf31nzuoHd6FuQ6vAsmXU/m6jW7Nll0wsxTm5Ynw5SoIrAbreJJmSSD8o2Shj
|
|
13
|
+
mvzWlurk1SgOhtoHP+AaVAn454x/Fn7+8u0f5ptNyQKBgQDvKc/3etCi22ntlgQM
|
|
14
|
+
dIwxbUPnmSvwejpMPOq3kMGTQWUJcKFinbPC28s/lJGzVjMsH5VisjiprCt3TXGN
|
|
15
|
+
XwmLNaUycWN3po9HyLSndAjHWM4aQ3yTOFKfLFEDUhKi8Dxy995KmHhgSSHd65qM
|
|
16
|
+
PvnPhITZWfKLkItCOmNEULxE8wKBgQDrZCji91ztgNyN3MHDGN2o854lAvWc1l6s
|
|
17
|
+
4L8ZwU/RCNYyvMkdhadASzLwVC44q/6txGjTHGjnJPdkzV2u+WsPVWG2uCaD9PV1
|
|
18
|
+
3L8wyG9sJ2YoJE5QcAQ+1J+rRN3fluzSKFiQNDpf7C5YqCPvk7C7lXFllGHM6FmF
|
|
19
|
+
hoYbyIb07wKBgQDKaF+qulViz0FqIwFQLT8NAcVrd7W5MyitpwyayLcbUkgZYioj
|
|
20
|
+
lQYzDuOH7swUtApg+GXsfpr39k9fC7rjg6BHIeKqu04MUHmIrjM+WTSoyd68WYtP
|
|
21
|
+
6WX7cn0py0ccgScXwfFuvnV6P8qaz7Afq5iuaSAp9zcPqQhCx7mFcrKzwwKBgQCo
|
|
22
|
+
VH3woNgxd59BS4a8j8GjmmOTMCSYPayCkE3Yiyca4ujaa6qek/9guOX6exh6qnR7
|
|
23
|
+
qyMTJRPXh9XqnfnKsM5grrwrwFC6uKf32x5WMl+LxjkFp8DhQNmoXMC554uK4xED
|
|
24
|
+
0JpUtSSxh+I0wDjCkKkn29y1uYCe2eF63RJ2N9ZavQKBgQCbHI/bMIZw/fsaijg3
|
|
25
|
+
676mpQqFPiQdXTogw8UWzZBAvP6eQOnqQ9aw4tJt6OZWnwCDMwXrEYk3H8qisZl7
|
|
26
|
+
ou63o5t8bTrU4C6e1VIQ1XuQM4FEpx6/TFwBG2FXkgzGHUpHlR9+woxqvmQgfsiW
|
|
27
|
+
tEJ/TZv7bU6s9FRLTzMspp+tIA==
|
|
28
|
+
-----END PRIVATE KEY-----
|
|
@@ -13,13 +13,15 @@
|
|
|
13
13
|
|
|
14
14
|
import logging
|
|
15
15
|
import ssl
|
|
16
|
-
from holado_core.common.exceptions.functional_exception import FunctionalException
|
|
17
16
|
from holado_core.common.tools.tools import Tools
|
|
18
17
|
from holado_json.ipc.json import set_object_attributes_with_json_dict
|
|
19
18
|
from holado_core.common.exceptions.technical_exception import TechnicalException
|
|
20
19
|
import os
|
|
21
20
|
import copy
|
|
22
21
|
from ssl import Purpose
|
|
22
|
+
from holado_system.system.command.command import Command, CommandStates
|
|
23
|
+
from holado.common.context.session_context import SessionContext
|
|
24
|
+
import json
|
|
23
25
|
|
|
24
26
|
logger = logging.getLogger(__name__)
|
|
25
27
|
|
|
@@ -32,37 +34,67 @@ class SslManager(object):
|
|
|
32
34
|
"""
|
|
33
35
|
|
|
34
36
|
@classmethod
|
|
35
|
-
def
|
|
37
|
+
def __get_path_manager(cls):
|
|
38
|
+
return SessionContext.instance().path_manager
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def new_ssl_context(cls, server_side=False, ssl_kwargs=None, context_kwargs=None):
|
|
36
42
|
res = None
|
|
37
43
|
|
|
38
|
-
if
|
|
39
|
-
|
|
40
|
-
kwargs = copy.copy(
|
|
44
|
+
if ssl_kwargs is None:
|
|
45
|
+
ssl_kwargs = {}
|
|
46
|
+
kwargs = copy.copy(ssl_kwargs)
|
|
41
47
|
|
|
42
48
|
try:
|
|
43
49
|
activate_ssl = kwargs.pop('activate', True)
|
|
44
50
|
if activate_ssl:
|
|
51
|
+
if Tools.has_sub_kwargs(kwargs, 'create_default_context.'):
|
|
52
|
+
create_default_context_kwargs = Tools.pop_sub_kwargs(kwargs, 'create_default_context.')
|
|
53
|
+
else:
|
|
54
|
+
create_default_context_kwargs = {}
|
|
55
|
+
|
|
45
56
|
purpose = Purpose.CLIENT_AUTH if server_side else Purpose.SERVER_AUTH
|
|
46
|
-
res = ssl.create_default_context(purpose=purpose)
|
|
57
|
+
res = ssl.create_default_context(purpose=purpose, **create_default_context_kwargs)
|
|
58
|
+
|
|
59
|
+
# res.set_default_verify_paths()
|
|
47
60
|
|
|
48
|
-
if
|
|
49
|
-
|
|
61
|
+
if context_kwargs:
|
|
62
|
+
c_kwargs = copy.copy(context_kwargs)
|
|
50
63
|
|
|
51
|
-
ciphers =
|
|
64
|
+
ciphers = c_kwargs.pop('ciphers', None)
|
|
52
65
|
if ciphers is not None:
|
|
66
|
+
if ciphers == 'OPENSSL_CIPHERS':
|
|
67
|
+
ciphers = SslManager.get_openssl_ciphers()
|
|
68
|
+
logger.debug(f"Using openssl ciphers: {ciphers}")
|
|
53
69
|
res.set_ciphers(ciphers)
|
|
54
|
-
if Tools.has_sub_kwargs(
|
|
55
|
-
load_cert_chain_kwargs = Tools.pop_sub_kwargs(
|
|
70
|
+
if Tools.has_sub_kwargs(c_kwargs, 'load_cert_chain.'):
|
|
71
|
+
load_cert_chain_kwargs = Tools.pop_sub_kwargs(c_kwargs, 'load_cert_chain.')
|
|
56
72
|
res.load_cert_chain(**load_cert_chain_kwargs)
|
|
57
|
-
if Tools.has_sub_kwargs(
|
|
58
|
-
load_verify_locations_kwargs = Tools.pop_sub_kwargs(
|
|
73
|
+
if Tools.has_sub_kwargs(c_kwargs, 'load_verify_locations.'):
|
|
74
|
+
load_verify_locations_kwargs = Tools.pop_sub_kwargs(c_kwargs, 'load_verify_locations.')
|
|
59
75
|
res.load_verify_locations(**load_verify_locations_kwargs)
|
|
60
76
|
|
|
61
|
-
# Set context attributes with remaining
|
|
62
|
-
if len(
|
|
63
|
-
set_object_attributes_with_json_dict(res,
|
|
77
|
+
# Set context attributes with remaining c_kwargs
|
|
78
|
+
if len(c_kwargs) > 0:
|
|
79
|
+
set_object_attributes_with_json_dict(res, c_kwargs)
|
|
80
|
+
|
|
81
|
+
logger.debug(f"Default verify paths: {ssl.get_default_verify_paths()})")
|
|
82
|
+
dvp = ssl.get_default_verify_paths()
|
|
83
|
+
if res.verify_mode != ssl.CERT_NONE and dvp.cafile is None and 'create_default_context.cafile' not in ssl_kwargs and 'load_verify_locations.cafile' not in context_kwargs:
|
|
84
|
+
msg_list = [f"No CA file is defined, it is not possible to verify certificates.",
|
|
85
|
+
f"Most common solutions:",
|
|
86
|
+
f" - Configure to not verify certificates, by adding/setting in table: | 'ssl.context.verify_mode' | ssl.CERT_NONE |",
|
|
87
|
+
f" - Be sure OpenSSL default CA file exists on system (configured path: '{dvp.openssl_cafile}'). Example of command (on Ubuntu): sudo ln -s /etc/ssl/certs/ca-certificates.crt {dvp.openssl_cafile}",
|
|
88
|
+
f" - Configure a CA file at context creation, by adding/setting in table: | 'ssl.create_default_context.cafile' | <path to CA file> |",
|
|
89
|
+
f" - Configure a CA file after context creation, by adding/setting in table: | 'ssl.context.load_verify_locations.cafile' | <path to CA file> |",
|
|
90
|
+
f"Note: step 'Given CACERTS_PATH = CA certs file path (from certifi package)' can be used to get the path to CA file coming with 'certifi' package that is supposed to be installed when using ssl module",
|
|
91
|
+
]
|
|
92
|
+
raise TechnicalException("\n".join(msg_list))
|
|
93
|
+
logger.debug(f"Loaded CA certificates: {res.get_ca_certs()})")
|
|
94
|
+
logger.debug(f"Loaded ciphers: {res.get_ciphers()})")
|
|
95
|
+
|
|
64
96
|
except Exception as exc:
|
|
65
|
-
msg = f"Failed to create SSL context with parameters {
|
|
97
|
+
msg = f"Failed to create SSL context with parameters ({ssl_kwargs}, {context_kwargs}): {exc}"
|
|
66
98
|
logger.error(msg)
|
|
67
99
|
raise TechnicalException(msg) from exc
|
|
68
100
|
|
|
@@ -73,9 +105,94 @@ class SslManager(object):
|
|
|
73
105
|
return res
|
|
74
106
|
|
|
75
107
|
@classmethod
|
|
76
|
-
def
|
|
108
|
+
def get_openssl_ciphers(cls):
|
|
109
|
+
import subprocess
|
|
110
|
+
output = subprocess.run(["openssl", "ciphers"], capture_output=True).stdout
|
|
111
|
+
output_str = output.decode("utf-8")
|
|
112
|
+
return output_str.strip().split("\n")[0]
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def get_default_certificates(cls):
|
|
116
|
+
dvp = ssl.get_default_verify_paths()
|
|
117
|
+
if not os.path.exists(dvp.cafile):
|
|
118
|
+
raise TechnicalException(f"openssl CA file '{dvp.cafile}' doesn't exist. Example of resolution command (on Ubuntu): sudo ln -s /etc/ssl/certs/ca-certificates.crt {dvp.cafile}")
|
|
119
|
+
return (dvp.cafile, dvp.capath)
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def get_certifi_ca_certs_file_path(cls):
|
|
123
|
+
import certifi
|
|
124
|
+
return certifi.where()
|
|
125
|
+
|
|
126
|
+
# @classmethod
|
|
127
|
+
# def get_localhost_certificate(cls):
|
|
128
|
+
# here = os.path.abspath(os.path.dirname(__file__))
|
|
129
|
+
# certfile_path = os.path.join(here, 'resources', 'certificates', 'localhost.crt')
|
|
130
|
+
# keyfile_path = os.path.join(here, 'resources', 'certificates', 'localhost.key')
|
|
131
|
+
# return (certfile_path, keyfile_path)
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def get_tcpbin_certificates(cls):
|
|
77
135
|
here = os.path.abspath(os.path.dirname(__file__))
|
|
78
|
-
certfile_path = os.path.join(here, 'resources', 'certificates', '
|
|
79
|
-
keyfile_path = os.path.join(here, 'resources', 'certificates', '
|
|
80
|
-
|
|
136
|
+
certfile_path = os.path.join(here, 'resources', 'certificates', 'tcpbin.crt')
|
|
137
|
+
keyfile_path = os.path.join(here, 'resources', 'certificates', 'tcpbin.key')
|
|
138
|
+
ca_certfile_path = os.path.join(here, 'resources', 'certificates', 'rootCACert.pem')
|
|
139
|
+
|
|
140
|
+
cls.ensure_tcpbin_certificates_are_valid(certfile_path, keyfile_path)
|
|
141
|
+
|
|
142
|
+
return (certfile_path, keyfile_path, ca_certfile_path)
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def ensure_tcpbin_certificates_are_valid(cls, public_key_path, private_key_path, duration_seconds=600):
|
|
146
|
+
cls.__get_path_manager().makedirs(public_key_path)
|
|
147
|
+
cls.__get_path_manager().makedirs(private_key_path)
|
|
148
|
+
|
|
149
|
+
# Verify if certificate has expired
|
|
150
|
+
do_generate_certificates = True
|
|
151
|
+
if os.path.exists(public_key_path):
|
|
152
|
+
cmd = f"openssl x509 -checkend {duration_seconds} -noout -in '{public_key_path}'"
|
|
153
|
+
command = Command(cmd, do_log_output=True, do_raise_on_stderr=False, executable="/bin/bash")
|
|
154
|
+
command.start()
|
|
155
|
+
command.join()
|
|
156
|
+
if command.state is CommandStates.Success:
|
|
157
|
+
do_generate_certificates = False
|
|
158
|
+
elif command.return_code == 1:
|
|
159
|
+
do_generate_certificates = True
|
|
160
|
+
else:
|
|
161
|
+
raise TechnicalException(f"Error while executing openssl command [{cmd}]: error code={command.return_code} ; stdout: [{command.stdout}] ; stderr: [{command.stderr}]")
|
|
162
|
+
|
|
163
|
+
# Generate new certificates if needed
|
|
164
|
+
if do_generate_certificates:
|
|
165
|
+
cmd = f"curl -s https://tcpbin.com/api/client-cert"
|
|
166
|
+
command = Command(cmd, do_log_output=True, do_raise_on_stderr=False, executable="/bin/bash")
|
|
167
|
+
command.start()
|
|
168
|
+
command.join()
|
|
169
|
+
if command.state is not CommandStates.Success:
|
|
170
|
+
raise TechnicalException(f"Error while executing command [{cmd}] : [{command.stderr}]")
|
|
171
|
+
|
|
172
|
+
data = json.loads(command.stdout)
|
|
173
|
+
with open(public_key_path, 'wt') as fout:
|
|
174
|
+
fout.write(data['cert'])
|
|
175
|
+
with open(private_key_path, 'wt') as fout:
|
|
176
|
+
fout.write(data['key'])
|
|
177
|
+
|
|
178
|
+
@classmethod
|
|
179
|
+
def generate_new_self_signed_key_files(cls, public_key_path, private_key_path, openssl_args):
|
|
180
|
+
cls.__get_path_manager().makedirs(public_key_path)
|
|
181
|
+
cls.__get_path_manager().makedirs(private_key_path)
|
|
182
|
+
|
|
183
|
+
cmd = f"openssl req -out '{public_key_path}' -keyout '{private_key_path}' {openssl_args}"
|
|
184
|
+
command = Command(cmd, do_log_output=True, do_raise_on_stderr=False, executable="/bin/bash")
|
|
185
|
+
command.start()
|
|
186
|
+
command.join()
|
|
187
|
+
|
|
188
|
+
if command.state is not CommandStates.Success:
|
|
189
|
+
raise TechnicalException(f"Error while executing openssl command [{cmd}] : [{command.stderr}]")
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
def generate_new_self_signed_key_files_for_localhost(cls, public_key_path, private_key_path, algorithm='rsa:2048'):
|
|
193
|
+
SslManager.generate_new_self_signed_key_files(
|
|
194
|
+
public_key_path, private_key_path,
|
|
195
|
+
f"-x509 -newkey {algorithm} -noenc -sha256 -subj '/CN=localhost' -extensions EXT \
|
|
196
|
+
-config <( printf \"[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth\")"
|
|
197
|
+
)
|
|
81
198
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
from holado_test.scenario.step_tools import StepTools
|
|
17
17
|
from holado.common.context.session_context import SessionContext
|
|
18
|
-
from holado_test.behave.behave import *
|
|
18
|
+
from holado_test.behave.behave import * # @UnusedWildImport
|
|
19
19
|
import logging
|
|
20
20
|
from holado_core.common.tools.string_tools import StrTools
|
|
21
21
|
|