From 5465e2d8fb50b97f8e1fa682c82c81613196cc0c Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Thu, 4 May 2023 17:58:23 -0400 Subject: Move auth tests to be new style filename, obj naming Also allow test task module selector to see new-style test modules --- tasks.py | 7 ++- tests/auth.py | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_auth.py | 136 ----------------------------------------------------- 3 files changed, 141 insertions(+), 138 deletions(-) create mode 100644 tests/auth.py delete mode 100644 tests/test_auth.py diff --git a/tasks.py b/tasks.py index 1f5e999c..f58f699e 100644 --- a/tasks.py +++ b/tasks.py @@ -1,4 +1,5 @@ import os +from pathlib import Path from os.path import join from shutil import rmtree, copytree @@ -50,8 +51,10 @@ def test( opts += " -f" modstr = "" if module is not None: - # NOTE: implicit test_ prefix as we're not on pytest-relaxed yet - modstr = " tests/test_{}.py".format(module) + base = f"{module}.py" + tests = Path("tests") + legacy = tests / f"test_{base}" + modstr = str(legacy if legacy.exists() else tests / base) # Switch runner depending on coverage or no coverage. # TODO: get pytest's coverage plugin working, IIRC it has issues? runner = "pytest" diff --git a/tests/auth.py b/tests/auth.py new file mode 100644 index 00000000..08de6148 --- /dev/null +++ b/tests/auth.py @@ -0,0 +1,136 @@ +# Copyright (C) 2008 Robey Pointer +# +# This file is part of paramiko. +# +# Paramiko is free software; you can redistribute it and/or modify it under the +# terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Paramiko; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Some unit tests for authenticating over a Transport. +""" + +import unittest +from pytest import raises + +from paramiko import ( + DSSKey, + BadAuthenticationType, + AuthenticationException, +) + +from ._util import _support, server, unicodey + + +class AuthHandler_: + def bad_auth_type(self): + """ + verify that we get the right exception when an unsupported auth + type is requested. + """ + # Server won't allow password auth for this user, so should fail + # and return just publickey allowed types + with server( + connect=dict(username="unknown", password="error"), + catch_error=True, + ) as (_, _, err): + assert isinstance(err, BadAuthenticationType) + assert err.allowed_types == ["publickey"] + + def bad_password(self): + """ + verify that a bad password gets the right exception, and that a retry + with the right password works. + """ + # NOTE: Transport.connect doesn't do any auth upfront if no userauth + # related kwargs given. + with server(defer=True) as (tc, ts): + # Auth once, badly + with raises(AuthenticationException): + tc.auth_password(username="slowdive", password="error") + # And again, correctly + tc.auth_password(username="slowdive", password="pygmalion") + + def multipart_auth(self): + """ + verify that multipart auth works. + """ + with server(defer=True) as (tc, ts): + assert tc.auth_password( + username="paranoid", password="paranoid" + ) == ["publickey"] + key = DSSKey.from_private_key_file(_support("dss.key")) + assert tc.auth_publickey(username="paranoid", key=key) == [] + + def interactive_auth(self): + """ + verify keyboard-interactive auth works. + """ + + def handler(title, instructions, prompts): + self.got_title = title + self.got_instructions = instructions + self.got_prompts = prompts + return ["cat"] + + with server(defer=True) as (tc, ts): + assert tc.auth_interactive("commie", handler) == [] + assert self.got_title == "password" + assert self.got_prompts == [("Password", False)] + + def interactive_fallback(self): + """ + verify that a password auth attempt will fallback to "interactive" + if password auth isn't supported but interactive is. + """ + with server(defer=True) as (tc, ts): + # This username results in an allowed_auth of just kbd-int, + # and has a configured interactive->response on the server. + assert tc.auth_password("commie", "cat") == [] + + def utf8(self): + """ + verify that utf-8 encoding happens in authentication. + """ + with server(defer=True) as (tc, ts): + assert tc.auth_password("utf8", unicodey) == [] + + def non_utf8(self): + """ + verify that non-utf-8 encoded passwords can be used for broken + servers. + """ + with server(defer=True) as (tc, ts): + assert tc.auth_password("non-utf8", "\xff") == [] + + def auth_exception_when_disconnected(self): + """ + verify that we catch a server disconnecting during auth, and report + it as an auth failure. + """ + with server(defer=True, skip_verify=True) as (tc, ts), raises( + AuthenticationException + ): + tc.auth_password("bad-server", "hello") + + def non_responsive_triggers_auth_exception(self): + """ + verify that authentication times out if server takes to long to + respond (or never responds). + """ + with server(defer=True, skip_verify=True) as (tc, ts), raises( + AuthenticationException + ) as info: + tc.auth_timeout = 1 # 1 second, to speed up test + tc.auth_password("unresponsive-server", "hello") + assert "Authentication timeout" in str(info.value) diff --git a/tests/test_auth.py b/tests/test_auth.py deleted file mode 100644 index 70ee0c36..00000000 --- a/tests/test_auth.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (C) 2008 Robey Pointer -# -# This file is part of paramiko. -# -# Paramiko is free software; you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation; either version 2.1 of the License, or (at your option) -# any later version. -# -# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR -# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with Paramiko; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -""" -Some unit tests for authenticating over a Transport. -""" - -import unittest -from pytest import raises - -from paramiko import ( - DSSKey, - BadAuthenticationType, - AuthenticationException, -) - -from ._util import _support, server, unicodey - - -class AuthTest(unittest.TestCase): - def test_bad_auth_type(self): - """ - verify that we get the right exception when an unsupported auth - type is requested. - """ - # Server won't allow password auth for this user, so should fail - # and return just publickey allowed types - with server( - connect=dict(username="unknown", password="error"), - catch_error=True, - ) as (_, _, err): - assert isinstance(err, BadAuthenticationType) - assert err.allowed_types == ["publickey"] - - def test_bad_password(self): - """ - verify that a bad password gets the right exception, and that a retry - with the right password works. - """ - # NOTE: Transport.connect doesn't do any auth upfront if no userauth - # related kwargs given. - with server(defer=True) as (tc, ts): - # Auth once, badly - with raises(AuthenticationException): - tc.auth_password(username="slowdive", password="error") - # And again, correctly - tc.auth_password(username="slowdive", password="pygmalion") - - def test_multipart_auth(self): - """ - verify that multipart auth works. - """ - with server(defer=True) as (tc, ts): - assert tc.auth_password( - username="paranoid", password="paranoid" - ) == ["publickey"] - key = DSSKey.from_private_key_file(_support("dss.key")) - assert tc.auth_publickey(username="paranoid", key=key) == [] - - def test_interactive_auth(self): - """ - verify keyboard-interactive auth works. - """ - - def handler(title, instructions, prompts): - self.got_title = title - self.got_instructions = instructions - self.got_prompts = prompts - return ["cat"] - - with server(defer=True) as (tc, ts): - assert tc.auth_interactive("commie", handler) == [] - assert self.got_title == "password" - assert self.got_prompts == [("Password", False)] - - def test_interactive_auth_fallback(self): - """ - verify that a password auth attempt will fallback to "interactive" - if password auth isn't supported but interactive is. - """ - with server(defer=True) as (tc, ts): - # This username results in an allowed_auth of just kbd-int, - # and has a configured interactive->response on the server. - assert tc.auth_password("commie", "cat") == [] - - def test_auth_utf8(self): - """ - verify that utf-8 encoding happens in authentication. - """ - with server(defer=True) as (tc, ts): - assert tc.auth_password("utf8", unicodey) == [] - - def test_auth_non_utf8(self): - """ - verify that non-utf-8 encoded passwords can be used for broken - servers. - """ - with server(defer=True) as (tc, ts): - assert tc.auth_password("non-utf8", "\xff") == [] - - def test_auth_gets_disconnected(self): - """ - verify that we catch a server disconnecting during auth, and report - it as an auth failure. - """ - with server(defer=True, skip_verify=True) as (tc, ts), raises( - AuthenticationException - ): - tc.auth_password("bad-server", "hello") - - def test_auth_non_responsive(self): - """ - verify that authentication times out if server takes to long to - respond (or never responds). - """ - with server(defer=True, skip_verify=True) as (tc, ts), raises( - AuthenticationException - ) as info: - tc.auth_timeout = 1 # 1 second, to speed up test - tc.auth_password("unresponsive-server", "hello") - assert "Authentication timeout" in str(info.value) -- cgit v1.2.1