Browse Source

added command line parameter --mqtt-password-file

Fabian Peter Hammerle 3 years ago
parent
commit
5e0e2a0124
4 changed files with 94 additions and 2 deletions
  1. 2 0
      CHANGELOG.md
  2. 8 0
      README.md
  3. 19 2
      switchbot_mqtt/__init__.py
  4. 65 0
      tests/test_cli.py

+ 2 - 0
CHANGELOG.md

@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### Added
+- Added command line parameter `--mqtt-password-file`
 
 ## [0.3.0] - 2020-05-08
 ### Added

+ 8 - 0
README.md

@@ -83,6 +83,14 @@ $ docker run --name spelunca_switchbot \
     switchbot-mqtt --mqtt-host HOSTNAME_OR_IP_ADDRESS
 ```
 
+## MQTT Authentication
+
+```sh
+switchbot-mqtt --mqtt-username me --mqtt-password secret …
+# or
+switchbot-mqtt --mqtt-username me --mqtt-password-file /var/lib/secrets/mqtt/password …
+```
+
 ## Alternatives
 
 * https://github.com/binsentsu/switchbot-ctrl

+ 19 - 2
switchbot_mqtt/__init__.py

@@ -19,6 +19,7 @@
 import argparse
 import enum
 import logging
+import pathlib
 import re
 import typing
 
@@ -197,11 +198,27 @@ def _main() -> None:
     argparser.add_argument("--mqtt-host", type=str, required=True)
     argparser.add_argument("--mqtt-port", type=int, default=1883)
     argparser.add_argument("--mqtt-username", type=str)
-    argparser.add_argument("--mqtt-password", type=str)
+    password_argument_group = argparser.add_mutually_exclusive_group()
+    password_argument_group.add_argument("--mqtt-password", type=str)
+    password_argument_group.add_argument(
+        "--mqtt-password-file",
+        type=pathlib.Path,
+        metavar="PATH",
+        dest="mqtt_password_path",
+        help="stripping trailing newline",
+    )
     args = argparser.parse_args()
+    if args.mqtt_password_path:
+        mqtt_password = args.mqtt_password_path.read_text()
+        if mqtt_password.endswith("\r\n"):
+            mqtt_password = mqtt_password[:-2]
+        elif mqtt_password.endswith("\n"):
+            mqtt_password = mqtt_password[:-1]
+    else:
+        mqtt_password = args.mqtt_password
     _run(
         mqtt_host=args.mqtt_host,
         mqtt_port=args.mqtt_port,
         mqtt_username=args.mqtt_username,
-        mqtt_password=args.mqtt_password,
+        mqtt_password=mqtt_password,
     )

+ 65 - 0
tests/test_cli.py

@@ -66,3 +66,68 @@ def test__main(
         mqtt_username=expected_username,
         mqtt_password=expected_password,
     )
+
+
+@pytest.mark.parametrize(
+    ("password_file_content", "expected_password",),
+    [
+        ("secret", "secret"),
+        ("secret space", "secret space"),
+        ("secret   ", "secret   "),
+        ("  secret ", "  secret "),
+        ("secret\n", "secret"),
+        ("secret\n\n", "secret\n"),
+        ("secret\r\n", "secret"),
+        ("secret\n\r\n", "secret\n"),
+    ],
+)
+def test__main_password_file(tmpdir, password_file_content, expected_password):
+    mqtt_password_path = tmpdir.join("mqtt-password")
+    with mqtt_password_path.open("w") as mqtt_password_file:
+        mqtt_password_file.write(password_file_content)
+    with unittest.mock.patch("switchbot_mqtt._run") as run_mock, unittest.mock.patch(
+        "sys.argv",
+        [
+            "",
+            "--mqtt-host",
+            "localhost",
+            "--mqtt-username",
+            "me",
+            "--mqtt-password-file",
+            str(mqtt_password_path),
+        ],
+    ):
+        # pylint: disable=protected-access
+        switchbot_mqtt._main()
+    run_mock.assert_called_once_with(
+        mqtt_host="localhost",
+        mqtt_port=1883,
+        mqtt_username="me",
+        mqtt_password=expected_password,
+    )
+
+
+def test__main_password_file_collision(capsys):
+    with unittest.mock.patch(
+        "sys.argv",
+        [
+            "",
+            "--mqtt-host",
+            "localhost",
+            "--mqtt-username",
+            "me",
+            "--mqtt-password",
+            "secret",
+            "--mqtt-password-file",
+            "/var/lib/secrets/mqtt/password",
+        ],
+    ):
+        with pytest.raises(SystemExit):
+            # pylint: disable=protected-access
+            switchbot_mqtt._main()
+    out, err = capsys.readouterr()
+    assert not out
+    assert (
+        "argument --mqtt-password-file: not allowed with argument --mqtt-password\n"
+        in err
+    )