From ffeb686fe0521d02f6421654087e518cf72fdabe Mon Sep 17 00:00:00 2001 From: Florian Brandes Date: Thu, 11 Jul 2024 18:53:48 +0200 Subject: [PATCH] add reading password from file Signed-off-by: Florian Brandes --- config.example | 3 ++- smtprd_ng/smtprd.py | 20 +++++++++++++++++++- tests/test_smtprd_ng.py | 24 +++++++++++++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/config.example b/config.example index 65e9037..b39ac16 100644 --- a/config.example +++ b/config.example @@ -6,7 +6,8 @@ hostname = smtp.mail.example.com port = 465 username = foo@example.com -password = s3cr3t +# If password_file is supplied, a valid path must be supplied +# password_file = Path/to/file sender = foo@example.com # set_reply_to = false # use_tls = true diff --git a/smtprd_ng/smtprd.py b/smtprd_ng/smtprd.py index fa277e8..216bb92 100644 --- a/smtprd_ng/smtprd.py +++ b/smtprd_ng/smtprd.py @@ -107,6 +107,22 @@ class Config: email_certs[email] = cert return email_certs + @classmethod + def _read_from_file(cls, file: Union[Path, str]) -> str: + """Read string from file""" + if len(file) == 0: + # empty string (which is our fallback), so return empty string + return "" + # file is non-empty and should provide a path + file = Path(file) + if not file.exists(): + raise OSError("File wasn't found: " + str(file)) + with open(file, "r", encoding="utf8") as fp: + line = fp.readline().strip("\n") + if len(line) == 0: + raise OSError("Empty file supplied: " + str(file)) + return line + @classmethod def _from_config(cls, config: configparser.RawConfigParser) -> "Config": email_certs = cls._get_emails_and_certs(config.items("emails")) @@ -120,7 +136,9 @@ class Config: port=config.getint("client", "port"), sender=config.get("client", "sender"), username=config.get("client", "username"), - password=config.get("client", "password"), + password=cls._read_from_file( + (config.get("client", "password_file", fallback="")) + ), set_reply_to=config.getboolean( "client", "set_reply_to", fallback=False ), diff --git a/tests/test_smtprd_ng.py b/tests/test_smtprd_ng.py index bb8075c..5aacd7f 100644 --- a/tests/test_smtprd_ng.py +++ b/tests/test_smtprd_ng.py @@ -19,7 +19,6 @@ Tests for smtprd_ng """ # pylint: disable=protected-access -# pylint: disable=unused-argument import configparser import email @@ -68,6 +67,7 @@ def test_config_from_config(): assert cfg.client.port == 465 assert cfg.client.smime_cert == "" assert cfg.client.use_tls is True + assert cfg.client.password == "" def test_config_from_ini(): @@ -80,6 +80,24 @@ def test_config_from_ini(): assert cfg.client.use_tls is True +def test_read_from_file(): + """Test read_from_file function and the fallback""" + existing_file = "config.example" + non_existing_file = "abcde" + empty_file = "tests/__init__.py" + empty_string = "" + assert smtprd.Config._read_from_file(existing_file) == "# [server]" + with pytest.raises(Exception) as e_info: + smtprd.Config._read_from_file(non_existing_file) + assert e_info.typename == "OSError" + assert str(e_info.value) == "File wasn't found: abcde" + assert smtprd.Config._read_from_file(empty_string) == "" + with pytest.raises(Exception) as e_info: + smtprd.Config._read_from_file(empty_file) + assert e_info.typename == "OSError" + assert str(e_info.value) == "Empty file supplied: tests/__init__.py" + + def test_config_emailcerts(): """test retrieval of email/cert pair""" email_list = [("email1", "cert1"), ("email2", "cert2")] @@ -153,7 +171,7 @@ def test_client_sign(): assert "Test to sign" in v.decode() -def test_cli_no_config_file(capsys): +def test_cli_no_config_file(): """Test whether config file parameter is supplied""" test_args = ["--config", ""] with pytest.raises(Exception) as e_info: @@ -162,7 +180,7 @@ def test_cli_no_config_file(capsys): assert str(e_info.value) == "No config file supplied" -def test_cli_config_file_not_found(capsys): +def test_cli_config_file_not_found(): """Test whether config file is found""" test_args = ["--config", "doesnotexist.conf"] with pytest.raises(Exception) as e_info: