From c1f720c150e9d30e10435561a70795b319424d3e Mon Sep 17 00:00:00 2001 From: Jordan Borean <jborean93@gmail.com> Date: Wed, 12 Jun 2024 08:58:25 +1000 Subject: [PATCH] Tidy up NTLM error messages and update changelog --- CHANGELOG.md | 4 +++- src/spnego/_ntlm.py | 8 +++++--- src/spnego/_version.py | 2 +- tests/test_ntlm.py | 16 ++++++++++++---- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fee354d..fed943d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # Changelog -## 0.10.3 - TBD +## 0.11.0 - TBD * Support input password string encoded with the `surrogatepass` error option * This allows the caller to provide a password for a gMSA or machine account that could contain invalid surrogate pairs for both NTLM and Kerberos auth. * Stop using deprecated `datetime.dateime.utcnow()` for CredSSP acceptor context +* Treat an empty string as a valid password, `None` is kept as use the cached credential +* Improve the exception shown when no password was provided and no cached credential was available ## 0.10.2 - 2023-10-04 diff --git a/src/spnego/_ntlm.py b/src/spnego/_ntlm.py index 99d5ed3..0d85231 100644 --- a/src/spnego/_ntlm.py +++ b/src/spnego/_ntlm.py @@ -135,7 +135,9 @@ def _get_credential( https://asecuritysite.com/encryption/lmhash """ if not store: - raise OperationNotAvailableError(context_msg="Retrieving NTLM store without NTLM_USER_FILE set to a filepath") + raise OperationNotAvailableError( + context_msg="No username or password was specified and the credential cache did not exist or contained no credentials" + ) domain = domain or "" @@ -178,7 +180,7 @@ def store_lines( else: raise SpnegoError( ErrorCode.failure, - context_msg="Failed to find any matching credential in " "NTLM_USER_FILE credential store.", + context_msg="Failed to find any matching credential in NTLM_USER_FILE credential store.", ) @@ -306,7 +308,7 @@ def __init__( # Make sure that the credential file is set and exists if not _get_credential_file(): raise OperationNotAvailableError( - context_msg="Retrieving NTLM store without NTLM_USER_FILE set to a " "filepath" + context_msg="NTLM acceptor requires NTLM credential cache to be provided through the env var NTLM_USER_FILE set to a filepath" ) self._temp_negotiate: typing.Optional[Negotiate] = None diff --git a/src/spnego/_version.py b/src/spnego/_version.py index 300b5f3..a7962ed 100644 --- a/src/spnego/_version.py +++ b/src/spnego/_version.py @@ -1,4 +1,4 @@ # Copyright: (c) 2020, Jordan Borean (@jborean93) <jborean93@gmail.com> # MIT License (see LICENSE or https://opensource.org/licenses/MIT) -__version__ = "0.10.3" +__version__ = "0.11.0" diff --git a/tests/test_ntlm.py b/tests/test_ntlm.py index 88fb2f4..0061614 100644 --- a/tests/test_ntlm.py +++ b/tests/test_ntlm.py @@ -168,12 +168,20 @@ def test_invalid_lm_compat_level(level, monkeypatch): ntlm.NTLMProxy("user", "pass") -@pytest.mark.parametrize("usage", ["initiate", "accept"]) -def test_context_no_store(usage): +def test_context_no_store_initiate(): with pytest.raises( - OperationNotAvailableError, match="Retrieving NTLM store without NTLM_USER_FILE set to a " "filepath" + OperationNotAvailableError, + match="No username or password was specified and the credential cache did not exist or contained no credentials", ): - ntlm.NTLMProxy(CredentialCache(), usage=usage) + ntlm.NTLMProxy(CredentialCache(), usage="initiate") + + +def test_context_no_store_accept(): + with pytest.raises( + OperationNotAvailableError, + match="NTLM acceptor requires NTLM credential cache to be provided through the env var NTLM_USER_FILE set to a filepath", + ): + ntlm.NTLMProxy(CredentialCache(), usage="accept") def test_iov_available():