Skip to content

Commit 831420a

Browse files
committed
change the behavior - insecure is default
also add tests
1 parent e95d9f2 commit 831420a

File tree

3 files changed

+151
-3
lines changed

3 files changed

+151
-3
lines changed

adafruit_minimqtt/adafruit_minimqtt.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def __init__(
171171
username=None,
172172
password=None,
173173
client_id=None,
174-
is_ssl=True,
174+
is_ssl=None,
175175
keep_alive=60,
176176
recv_timeout=10,
177177
socket_pool=None,
@@ -220,9 +220,14 @@ def __init__(
220220
): # [MQTT-3.1.3.5]
221221
raise MMQTTException("Password length is too large.")
222222

223+
# The connection will be insecure unless is_ssl is set to True.
224+
# If the port is not specified, the security will be set based on the is_ssl parameter.
225+
# If the port is specified, the is_ssl parameter will be honored.
223226
self.port = MQTT_TCP_PORT
224-
if is_ssl:
225-
self._is_ssl = True
227+
if is_ssl is None:
228+
is_ssl = False
229+
self._is_ssl = is_ssl
230+
if self._is_ssl:
226231
self.port = MQTT_TLS_PORT
227232
if port:
228233
self.port = port

tests/test_port_ssl.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# SPDX-FileCopyrightText: 2023 Vladimír Kotal
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
5+
"""tests that verify the connect behavior w.r.t. port number and TLS"""
6+
7+
import socket
8+
import ssl
9+
from unittest import TestCase, main
10+
from unittest.mock import Mock, call, patch
11+
12+
import adafruit_minimqtt.adafruit_minimqtt as MQTT
13+
14+
15+
class PortSslSetup(TestCase):
16+
"""This class contains tests that verify how host/port and TLS is set for connect().
17+
These tests assume that there is no MQTT broker running on the hosts/ports they connect to.
18+
"""
19+
20+
def test_default_port(self) -> None:
21+
"""verify default port value and that TLS is not used"""
22+
host = "127.0.0.1"
23+
port = 1883
24+
25+
with patch.object(socket.socket, "connect") as connect_mock:
26+
ssl_context = ssl.create_default_context()
27+
mqtt_client = MQTT.MQTT(
28+
broker=host,
29+
socket_pool=socket,
30+
ssl_context=ssl_context,
31+
connect_retries=1,
32+
)
33+
34+
ssl_mock = Mock()
35+
ssl_context.wrap_socket = ssl_mock
36+
37+
with self.assertRaises(OSError):
38+
expected_port = port
39+
mqtt_client.connect()
40+
41+
ssl_mock.assert_not_called()
42+
connect_mock.assert_called()
43+
# Assuming the repeated calls will have the same arguments.
44+
connect_mock.assert_has_calls(
45+
[call((host, expected_port))]
46+
)
47+
48+
def test_connect_override(self):
49+
"""Test that connect() can override host and port."""
50+
host = "127.0.0.1"
51+
port = 1883
52+
53+
with patch.object(socket.socket, "connect") as connect_mock:
54+
connect_mock.side_effect = OSError("artificial error")
55+
mqtt_client = MQTT.MQTT(
56+
broker=host,
57+
port=port,
58+
socket_pool=socket,
59+
connect_retries=1,
60+
)
61+
62+
with self.assertRaises(OSError):
63+
expected_host = "127.0.0.2"
64+
expected_port = 1884
65+
self.assertNotEqual(
66+
expected_port, port, "port override should differ"
67+
)
68+
self.assertNotEqual(
69+
expected_host, host, "host override should differ"
70+
)
71+
mqtt_client.connect(host=expected_host, port=expected_port)
72+
73+
connect_mock.assert_called()
74+
# Assuming the repeated calls will have the same arguments.
75+
connect_mock.assert_has_calls(
76+
[call((expected_host, expected_port))]
77+
)
78+
79+
def test_tls_port(self) -> None:
80+
"""verify that when is_ssl=True is set, the default port is 8883
81+
and the socket is TLS wrapped. Also test that the TLS port can be overridden."""
82+
host = "127.0.0.1"
83+
84+
for port in [None, 8884]:
85+
if port is None:
86+
expected_port = 8883
87+
else:
88+
expected_port = port
89+
with self.subTest():
90+
ssl_mock = Mock()
91+
mqtt_client = MQTT.MQTT(
92+
broker=host,
93+
port=port,
94+
socket_pool=socket,
95+
is_ssl=True,
96+
ssl_context=ssl_mock,
97+
connect_retries=1,
98+
)
99+
100+
socket_mock = Mock()
101+
connect_mock = Mock(side_effect=OSError)
102+
socket_mock.connect = connect_mock
103+
ssl_mock.wrap_socket = Mock(return_value=socket_mock)
104+
105+
with self.assertRaises(RuntimeError):
106+
mqtt_client.connect()
107+
108+
ssl_mock.wrap_socket.assert_called()
109+
110+
connect_mock.assert_called()
111+
# Assuming the repeated calls will have the same arguments.
112+
connect_mock.assert_has_calls([call((host, expected_port))])
113+
114+
def test_tls_without_ssl_context(self) -> None:
115+
"""verify that when is_ssl=True is set, the code will check that ssl_context is not None"""
116+
host = "127.0.0.1"
117+
118+
mqtt_client = MQTT.MQTT(
119+
broker=host,
120+
socket_pool=socket,
121+
is_ssl=True,
122+
ssl_context=None,
123+
connect_retries=1,
124+
)
125+
126+
with self.assertRaises(RuntimeError) as context:
127+
mqtt_client.connect()
128+
self.assertTrue("ssl_context must be set" in str(context))
129+
130+
131+
if __name__ == "__main__":
132+
main()

tox.ini

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-FileCopyrightText: 2023 Vladimír Kotal
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
[tox]
6+
envlist = py39
7+
8+
[testenv]
9+
changedir = {toxinidir}/tests
10+
deps = pytest==6.2.5
11+
commands = pytest -v

0 commit comments

Comments
 (0)