diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/configs/basic (renamed from tests/configs/basic.config) | 1 | ||||
-rw-r--r-- | tests/configs/canon (renamed from tests/configs/canon.config) | 1 | ||||
-rw-r--r-- | tests/configs/canon-always (renamed from tests/configs/canon-always.config) | 1 | ||||
-rw-r--r-- | tests/configs/canon-ipv4 (renamed from tests/configs/canon-ipv4.config) | 1 | ||||
-rw-r--r-- | tests/configs/canon-local (renamed from tests/configs/canon-local.config) | 1 | ||||
-rw-r--r-- | tests/configs/canon-local-always (renamed from tests/configs/canon-local-always.config) | 1 | ||||
-rw-r--r-- | tests/configs/deep-canon (renamed from tests/configs/deep-canon.config) | 1 | ||||
-rw-r--r-- | tests/configs/deep-canon-maxdots (renamed from tests/configs/deep-canon-maxdots.config) | 1 | ||||
-rw-r--r-- | tests/configs/empty-canon (renamed from tests/configs/empty-canon.config) | 1 | ||||
-rw-r--r-- | tests/configs/fallback-no (renamed from tests/configs/fallback-no.config) | 1 | ||||
-rw-r--r-- | tests/configs/fallback-yes (renamed from tests/configs/fallback-yes.config) | 1 | ||||
-rw-r--r-- | tests/configs/invalid | 1 | ||||
-rw-r--r-- | tests/configs/match-all | 2 | ||||
-rw-r--r-- | tests/configs/match-all-after-canonical | 6 | ||||
-rw-r--r-- | tests/configs/match-all-and-more | 3 | ||||
-rw-r--r-- | tests/configs/match-all-and-more-before | 3 | ||||
-rw-r--r-- | tests/configs/match-all-before-canonical | 6 | ||||
-rw-r--r-- | tests/configs/match-canonical-no | 7 | ||||
-rw-r--r-- | tests/configs/match-canonical-yes | 6 | ||||
-rw-r--r-- | tests/configs/match-complex | 17 | ||||
-rw-r--r-- | tests/configs/match-exec | 8 | ||||
-rw-r--r-- | tests/configs/match-host | 3 | ||||
-rw-r--r-- | tests/configs/match-host-canonicalized | 10 | ||||
-rw-r--r-- | tests/configs/match-host-from-match | 6 | ||||
-rw-r--r-- | tests/configs/match-host-glob | 4 | ||||
-rw-r--r-- | tests/configs/match-host-glob-list | 10 | ||||
-rw-r--r-- | tests/configs/match-host-name | 5 | ||||
-rw-r--r-- | tests/configs/match-host-negated | 2 | ||||
-rw-r--r-- | tests/configs/match-host-no-arg | 3 | ||||
-rw-r--r-- | tests/configs/match-localuser | 14 | ||||
-rw-r--r-- | tests/configs/match-localuser-no-arg | 2 | ||||
-rw-r--r-- | tests/configs/match-orighost | 16 | ||||
-rw-r--r-- | tests/configs/match-orighost-canonical | 5 | ||||
-rw-r--r-- | tests/configs/match-orighost-no-arg | 3 | ||||
-rw-r--r-- | tests/configs/match-user | 14 | ||||
-rw-r--r-- | tests/configs/match-user-explicit | 4 | ||||
-rw-r--r-- | tests/configs/match-user-no-arg | 2 | ||||
-rw-r--r-- | tests/configs/multi-canon-domains (renamed from tests/configs/multi-canon-domains.config) | 1 | ||||
-rw-r--r-- | tests/configs/no-canon (renamed from tests/configs/no-canon.config) | 1 | ||||
-rw-r--r-- | tests/configs/robey (renamed from tests/configs/robey.config) | 1 | ||||
-rw-r--r-- | tests/configs/zero-maxdots (renamed from tests/configs/zero-maxdots.config) | 1 | ||||
-rw-r--r-- | tests/test_config.py | 455 | ||||
-rw-r--r-- | tests/test_util.py | 1 | ||||
-rw-r--r-- | tests/util.py | 2 |
44 files changed, 563 insertions, 72 deletions
diff --git a/tests/configs/basic.config b/tests/configs/basic index 1ae37cc6..73872b47 100644 --- a/tests/configs/basic.config +++ b/tests/configs/basic @@ -3,4 +3,3 @@ CanonicalDomains paramiko.org Host www.paramiko.org User rando -# vim: set ft=sshconfig : diff --git a/tests/configs/canon.config b/tests/configs/canon index 7a7ce6c6..3a3cc66a 100644 --- a/tests/configs/canon.config +++ b/tests/configs/canon @@ -8,4 +8,3 @@ Host www.paramiko.org IdentityFile canonicalized.key -# vim: set ft=sshconfig : diff --git a/tests/configs/canon-always.config b/tests/configs/canon-always index 85058a14..fdaeabd4 100644 --- a/tests/configs/canon-always.config +++ b/tests/configs/canon-always @@ -5,4 +5,3 @@ Host www.paramiko.org User rando -# vim: set ft=sshconfig : diff --git a/tests/configs/canon-ipv4.config b/tests/configs/canon-ipv4 index 9f48273e..b29766a3 100644 --- a/tests/configs/canon-ipv4.config +++ b/tests/configs/canon-ipv4 @@ -6,4 +6,3 @@ Host www.paramiko.org User rando -# vim: set ft=sshconfig : diff --git a/tests/configs/canon-local.config b/tests/configs/canon-local index 418f7723..0b5588ca 100644 --- a/tests/configs/canon-local.config +++ b/tests/configs/canon-local @@ -6,4 +6,3 @@ Host www CanonicalizeHostname yes -# vim: set ft=sshconfig : diff --git a/tests/configs/canon-local-always.config b/tests/configs/canon-local-always index c821d113..5c059ae1 100644 --- a/tests/configs/canon-local-always.config +++ b/tests/configs/canon-local-always @@ -6,4 +6,3 @@ Host www CanonicalizeHostname always -# vim: set ft=sshconfig : diff --git a/tests/configs/deep-canon.config b/tests/configs/deep-canon index 3c111f48..bb3ed5ad 100644 --- a/tests/configs/deep-canon.config +++ b/tests/configs/deep-canon @@ -10,4 +10,3 @@ Host sub.www.paramiko.org Host subber.sub.www.paramiko.org User deeper -# vim: set ft=sshconfig : diff --git a/tests/configs/deep-canon-maxdots.config b/tests/configs/deep-canon-maxdots index 37a82e72..9262cc58 100644 --- a/tests/configs/deep-canon-maxdots.config +++ b/tests/configs/deep-canon-maxdots @@ -11,4 +11,3 @@ Host sub.www.paramiko.org Host subber.sub.www.paramiko.org User deeper -# vim: set ft=sshconfig : diff --git a/tests/configs/empty-canon.config b/tests/configs/empty-canon index f268a2ca..d29b30fa 100644 --- a/tests/configs/empty-canon.config +++ b/tests/configs/empty-canon @@ -6,4 +6,3 @@ Host www.paramiko.org User rando -# vim: set ft=sshconfig : diff --git a/tests/configs/fallback-no.config b/tests/configs/fallback-no index 86b6a484..d68bfe66 100644 --- a/tests/configs/fallback-no.config +++ b/tests/configs/fallback-no @@ -6,4 +6,3 @@ Host www.paramiko.org User rando -# vim: set ft=sshconfig : diff --git a/tests/configs/fallback-yes.config b/tests/configs/fallback-yes index a07064a0..a87764a8 100644 --- a/tests/configs/fallback-yes.config +++ b/tests/configs/fallback-yes @@ -5,4 +5,3 @@ CanonicalizeFallbackLocal yes Host www.paramiko.org User rando -# vim: set ft=sshconfig : diff --git a/tests/configs/invalid b/tests/configs/invalid new file mode 100644 index 00000000..81332fe8 --- /dev/null +++ b/tests/configs/invalid @@ -0,0 +1 @@ +lolwut diff --git a/tests/configs/match-all b/tests/configs/match-all new file mode 100644 index 00000000..7673e0a0 --- /dev/null +++ b/tests/configs/match-all @@ -0,0 +1,2 @@ +Match all + User awesome diff --git a/tests/configs/match-all-after-canonical b/tests/configs/match-all-after-canonical new file mode 100644 index 00000000..2acc3a6e --- /dev/null +++ b/tests/configs/match-all-after-canonical @@ -0,0 +1,6 @@ +CanonicalizeHostname yes +CanonicalDomains paramiko.org + +Match canonical all + User awesome + diff --git a/tests/configs/match-all-and-more b/tests/configs/match-all-and-more new file mode 100644 index 00000000..2281d238 --- /dev/null +++ b/tests/configs/match-all-and-more @@ -0,0 +1,3 @@ +Match all exec "lol nope" + HostName whatever + diff --git a/tests/configs/match-all-and-more-before b/tests/configs/match-all-and-more-before new file mode 100644 index 00000000..89a737ee --- /dev/null +++ b/tests/configs/match-all-and-more-before @@ -0,0 +1,3 @@ +Match exec "lol nope" all + HostName whatever + diff --git a/tests/configs/match-all-before-canonical b/tests/configs/match-all-before-canonical new file mode 100644 index 00000000..fe0e6646 --- /dev/null +++ b/tests/configs/match-all-before-canonical @@ -0,0 +1,6 @@ +CanonicalizeHostname yes +CanonicalDomains paramiko.org + +Match all canonical + User oops + diff --git a/tests/configs/match-canonical-no b/tests/configs/match-canonical-no new file mode 100644 index 00000000..e528dc64 --- /dev/null +++ b/tests/configs/match-canonical-no @@ -0,0 +1,7 @@ +CanonicalizeHostname no + +Match canonical all + User awesome + +Match !canonical host specific + User overload diff --git a/tests/configs/match-canonical-yes b/tests/configs/match-canonical-yes new file mode 100644 index 00000000..5c5759c7 --- /dev/null +++ b/tests/configs/match-canonical-yes @@ -0,0 +1,6 @@ +CanonicalizeHostname yes +CanonicalDomains paramiko.org + +Match !canonical host www* + User hidden + diff --git a/tests/configs/match-complex b/tests/configs/match-complex new file mode 100644 index 00000000..63634039 --- /dev/null +++ b/tests/configs/match-complex @@ -0,0 +1,17 @@ +HostName bogus + +Match originalhost target host bogus + User rand + +Match originalhost remote localuser rando + User calrissian + +# Just to set user for subsequent match +Match originalhost www + User calrissian + +Match !canonical originalhost www host bogus localuser rando user calrissian + Port 7777 + +Match !canonical !originalhost www host bogus localuser rando !user calrissian + Port 1234 diff --git a/tests/configs/match-exec b/tests/configs/match-exec new file mode 100644 index 00000000..88d3f769 --- /dev/null +++ b/tests/configs/match-exec @@ -0,0 +1,8 @@ +Match exec "quoted" + User benjamin + +Match exec unquoted + User rando + +Match exec "quoted spaced" + User neil diff --git a/tests/configs/match-host b/tests/configs/match-host new file mode 100644 index 00000000..8259fc6b --- /dev/null +++ b/tests/configs/match-host @@ -0,0 +1,3 @@ +Match host target + User rand + diff --git a/tests/configs/match-host-canonicalized b/tests/configs/match-host-canonicalized new file mode 100644 index 00000000..347242a0 --- /dev/null +++ b/tests/configs/match-host-canonicalized @@ -0,0 +1,10 @@ +CanonicalizeHostname yes +CanonicalDomains paramiko.org + +Match host www.paramiko.org + User rand + +Match canonical host docs.paramiko.org + User eric + + diff --git a/tests/configs/match-host-from-match b/tests/configs/match-host-from-match new file mode 100644 index 00000000..64b4c4b5 --- /dev/null +++ b/tests/configs/match-host-from-match @@ -0,0 +1,6 @@ +Match host original-host + HostName substituted-host + +Match host substituted-host + User inner + diff --git a/tests/configs/match-host-glob b/tests/configs/match-host-glob new file mode 100644 index 00000000..9c198ce3 --- /dev/null +++ b/tests/configs/match-host-glob @@ -0,0 +1,4 @@ +Match host *ever + User matrim + + diff --git a/tests/configs/match-host-glob-list b/tests/configs/match-host-glob-list new file mode 100644 index 00000000..76796777 --- /dev/null +++ b/tests/configs/match-host-glob-list @@ -0,0 +1,10 @@ +Match host *ever + User matrim + +Match host somehost,someotherhost + User thom + +Match host goo*,!goof + User perrin + + diff --git a/tests/configs/match-host-name b/tests/configs/match-host-name new file mode 100644 index 00000000..5b4adb84 --- /dev/null +++ b/tests/configs/match-host-name @@ -0,0 +1,5 @@ +HostName default-host + +Match host default-host + User silly + diff --git a/tests/configs/match-host-negated b/tests/configs/match-host-negated new file mode 100644 index 00000000..7c5d3f3e --- /dev/null +++ b/tests/configs/match-host-negated @@ -0,0 +1,2 @@ +Match !host www + User jeff diff --git a/tests/configs/match-host-no-arg b/tests/configs/match-host-no-arg new file mode 100644 index 00000000..e9936844 --- /dev/null +++ b/tests/configs/match-host-no-arg @@ -0,0 +1,3 @@ +Match host + User oops + diff --git a/tests/configs/match-localuser b/tests/configs/match-localuser new file mode 100644 index 00000000..fe4a276c --- /dev/null +++ b/tests/configs/match-localuser @@ -0,0 +1,14 @@ +Match localuser gandalf + HostName gondor + +Match localuser b* + HostName shire + +Match localuser aragorn,frodo + HostName moria + +Match localuser gimli,!legolas + Port 7373 + +Match !localuser sauron + HostName mordor diff --git a/tests/configs/match-localuser-no-arg b/tests/configs/match-localuser-no-arg new file mode 100644 index 00000000..6623553a --- /dev/null +++ b/tests/configs/match-localuser-no-arg @@ -0,0 +1,2 @@ +Match localuser + User oops diff --git a/tests/configs/match-orighost b/tests/configs/match-orighost new file mode 100644 index 00000000..10541993 --- /dev/null +++ b/tests/configs/match-orighost @@ -0,0 +1,16 @@ +HostName bogus + +Match originalhost target + User tuon + +Match originalhost what* + User matrim + +Match originalhost comma,sep* + User chameleon + +Match originalhost yep,!nope + User skipped + +Match !originalhost www !originalhost nope + User thom diff --git a/tests/configs/match-orighost-canonical b/tests/configs/match-orighost-canonical new file mode 100644 index 00000000..737345e8 --- /dev/null +++ b/tests/configs/match-orighost-canonical @@ -0,0 +1,5 @@ +CanonicalizeHostname yes +CanonicalDomains paramiko.org + +Match originalhost www + User tuon diff --git a/tests/configs/match-orighost-no-arg b/tests/configs/match-orighost-no-arg new file mode 100644 index 00000000..ebf81fa0 --- /dev/null +++ b/tests/configs/match-orighost-no-arg @@ -0,0 +1,3 @@ +Match originalhost + User oops + diff --git a/tests/configs/match-user b/tests/configs/match-user new file mode 100644 index 00000000..14d6ac12 --- /dev/null +++ b/tests/configs/match-user @@ -0,0 +1,14 @@ +Match user gandalf + HostName gondor + +Match user b* + HostName shire + +Match user aragorn,frodo + HostName moria + +Match user gimli,!legolas + Port 7373 + +Match !user sauron + HostName mordor diff --git a/tests/configs/match-user-explicit b/tests/configs/match-user-explicit new file mode 100644 index 00000000..9a2b1d82 --- /dev/null +++ b/tests/configs/match-user-explicit @@ -0,0 +1,4 @@ +User explicit + +Match user explicit + HostName dumb diff --git a/tests/configs/match-user-no-arg b/tests/configs/match-user-no-arg new file mode 100644 index 00000000..65a11ab4 --- /dev/null +++ b/tests/configs/match-user-no-arg @@ -0,0 +1,2 @@ +Match user + User oops diff --git a/tests/configs/multi-canon-domains.config b/tests/configs/multi-canon-domains index f0cf521d..0fe98e31 100644 --- a/tests/configs/multi-canon-domains.config +++ b/tests/configs/multi-canon-domains @@ -5,4 +5,3 @@ Host www.paramiko.org User rando -# vim: set ft=sshconfig : diff --git a/tests/configs/no-canon.config b/tests/configs/no-canon index bd48b790..62e8f713 100644 --- a/tests/configs/no-canon.config +++ b/tests/configs/no-canon @@ -5,4 +5,3 @@ Host www.paramiko.org User rando -# vim: set ft=sshconfig : diff --git a/tests/configs/robey.config b/tests/configs/robey index 2175182f..b2026224 100644 --- a/tests/configs/robey.config +++ b/tests/configs/robey @@ -15,4 +15,3 @@ Host * Host spoo.example.com Crazy something else -# vim: set ft=sshconfig list : diff --git a/tests/configs/zero-maxdots.config b/tests/configs/zero-maxdots index c7a095ab..eae90285 100644 --- a/tests/configs/zero-maxdots.config +++ b/tests/configs/zero-maxdots @@ -8,4 +8,3 @@ Host www.paramiko.org Host sub.www.paramiko.org User deep -# vim: set ft=sshconfig : diff --git a/tests/test_config.py b/tests/test_config.py index f8312b12..bc700f94 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -4,15 +4,47 @@ from os.path import expanduser from socket import gaierror +from paramiko.py3compat import string_types + +from invoke import Result from mock import patch from pytest import raises, mark, fixture -from paramiko import SSHConfig, SSHConfigDict, CouldNotCanonicalize -from paramiko.util import lookup_ssh_host_config +from paramiko import ( + SSHConfig, + SSHConfigDict, + CouldNotCanonicalize, + ConfigParseError, +) from .util import _config +@fixture +def socket(): + """ + Patch all of socket.* in our config module to prevent eg real DNS lookups. + + Also forces getaddrinfo (used in our addressfamily lookup stuff) to always + fail by default to mimic usual lack of AddressFamily related crap. + + Callers who want to mock DNS lookups can then safely assume gethostbyname() + will be in use. + """ + with patch("paramiko.config.socket") as mocket: + # Reinstate gaierror as an actual exception and not a sub-mock. + # (Presumably this would work with any exception, but why not use the + # real one?) + mocket.gaierror = gaierror + # Patch out getaddrinfo, used to detect family-specific IP lookup - + # only useful for a few specific tests. + mocket.getaddrinfo.side_effect = mocket.gaierror + # Patch out getfqdn to return some real string for when it gets called; + # some code (eg tokenization) gets mad w/ MagicMocks + mocket.getfqdn.return_value = "some.fake.fqdn" + yield mocket + + def load_config(name): return SSHConfig.from_path(_config(name)) @@ -61,41 +93,42 @@ class TestSSHConfig(object): ] assert self.config._config == expected - @mark.parametrize("host,values", ( - ( - "irc.danger.com", - { - "crazy": "something dumb", - "hostname": "irc.danger.com", - "user": "robey", - }, - ), - ( - "irc.example.com", - { - "crazy": "something dumb", - "hostname": "irc.example.com", - "user": "robey", - "port": "3333", - }, - ), + @mark.parametrize( + "host,values", ( - "spoo.example.com", - { - "crazy": "something dumb", - "hostname": "spoo.example.com", - "user": "robey", - "port": "3333", - }, + ( + "irc.danger.com", + { + "crazy": "something dumb", + "hostname": "irc.danger.com", + "user": "robey", + }, + ), + ( + "irc.example.com", + { + "crazy": "something dumb", + "hostname": "irc.example.com", + "user": "robey", + "port": "3333", + }, + ), + ( + "spoo.example.com", + { + "crazy": "something dumb", + "hostname": "spoo.example.com", + "user": "robey", + "port": "3333", + }, + ), ), - )) + ) def test_host_config(self, host, values): expected = dict( - values, - hostname=host, - identityfile=[expanduser("~/.ssh/id_rsa")], + values, hostname=host, identityfile=[expanduser("~/.ssh/id_rsa")] ) - assert lookup_ssh_host_config(host, self.config) == expected + assert self.config.lookup(host) == expected def test_fabric_issue_33(self): config = SSHConfig.from_text( @@ -112,7 +145,7 @@ Host * ) host = "www13.example.com" expected = {"hostname": host, "port": "22"} - assert lookup_ssh_host_config(host, config) == expected + assert config.lookup(host) == expected def test_proxycommand_config_equals_parsing(self): """ @@ -128,7 +161,7 @@ Host equals-delimited """ ) for host in ("space-delimited", "equals-delimited"): - value = lookup_ssh_host_config(host, config)["proxycommand"] + value = config.lookup(host)["proxycommand"] assert value == "foo bar=biz baz" def test_proxycommand_interpolation(self): @@ -154,7 +187,7 @@ Host * ("specific", "host specific port 37 lol"), ("portonly", "host portonly port 155"), ): - assert lookup_ssh_host_config(host, config)["proxycommand"] == val + assert config.lookup(host)["proxycommand"] == val def test_proxycommand_tilde_expansion(self): """ @@ -169,9 +202,30 @@ Host test expected = "ssh -F {}/.ssh/test_config bastion nc test 22".format( expanduser("~") ) - got = lookup_ssh_host_config("test", config)["proxycommand"] + got = config.lookup("test")["proxycommand"] assert got == expected + @patch("paramiko.config.getpass") + def test_controlpath_token_expansion(self, getpass): + getpass.getuser.return_value = "gandalf" + config = SSHConfig.from_text( + """ +Host explicit_user + User root + ControlPath user %u remoteuser %r + +Host explicit_host + HostName ohai + ControlPath remoteuser %r host %h orighost %n + """ + ) + result = config.lookup("explicit_user")["controlpath"] + # Remote user is User val, local user is User val + assert result == "user gandalf remoteuser root" + result = config.lookup("explicit_host")["controlpath"] + # Remote user falls back to local user; host and orighost may differ + assert result == "remoteuser gandalf host ohai orighost explicit_host" + def test_negation(self): config = SSHConfig.from_text( """ @@ -190,7 +244,7 @@ Host * ) host = "www13.example.com" expected = {"hostname": host, "port": "8080"} - assert lookup_ssh_host_config(host, config) == expected + assert config.lookup(host) == expected def test_proxycommand(self): config = SSHConfig.from_text( @@ -220,7 +274,7 @@ ProxyCommand foo=bar:%h-%p }, }.items(): - assert lookup_ssh_host_config(host, config) == values + assert config.lookup(host) == values def test_identityfile(self): config = SSHConfig.from_text( @@ -250,7 +304,7 @@ IdentityFile id_dsa22 }, }.items(): - assert lookup_ssh_host_config(host, config) == values + assert config.lookup(host) == values def test_config_addressfamily_and_lazy_fqdn(self): """ @@ -308,7 +362,7 @@ Host param4 "p a r" "p" "par" para "para": {"hostname": "para", "port": "4444"}, } for host, values in res.items(): - assert lookup_ssh_host_config(host, config) == values + assert config.lookup(host) == values def test_quoted_params_in_config(self): config = SSHConfig.from_text( @@ -339,7 +393,7 @@ Host param3 parara }, } for host, values in res.items(): - assert lookup_ssh_host_config(host, config) == values + assert config.lookup(host) == values def test_quoted_host_in_config(self): conf = SSHConfig() @@ -360,9 +414,13 @@ Host param3 parara for host, values in correct_data.items(): assert conf._get_hosts(host) == values for host in incorrect_data: - with raises(Exception): + with raises(ConfigParseError): conf._get_hosts(host) + def test_invalid_line_format_excepts(self): + with raises(ConfigParseError): + load_config("invalid") + def test_proxycommand_none_issue_418(self): config = SSHConfig.from_text( """ @@ -382,7 +440,7 @@ Host proxycommand-with-equals-none }, }.items(): - assert lookup_ssh_host_config(host, config) == values + assert config.lookup(host) == values def test_proxycommand_none_masking(self): # Re: https://github.com/paramiko/paramiko/issues/670 @@ -469,19 +527,6 @@ Host * assert config.lookup("anything-else").as_int("port") == 3333 -@fixture -def socket(): - with patch("paramiko.config.socket") as mocket: - # Reinstate gaierror as an actual exception and not a sub-mock. - # (Presumably this would work with any exception, but why not use the - # real one?) - mocket.gaierror = gaierror - # Patch out getaddrinfo, used to detect family-specific IP lookup - - # only useful for a few specific tests. - mocket.getaddrinfo.side_effect = mocket.gaierror - yield mocket - - class TestHostnameCanonicalization(object): # NOTE: this class uses on-disk configs, and ones with real (at time of # writing) DNS names, so that one can easily test OpenSSH's behavior using @@ -605,3 +650,301 @@ class TestCanonicalizationOfCNAMEs(object): def test_permitted_cnames_may_be_multiple_complex_mappings(self): # Same as prev but with multiple patterns on both ends in both args pass + + +class TestMatchAll(object): + def test_always_matches(self): + result = load_config("match-all").lookup("general") + assert result["user"] == "awesome" + + def test_may_not_mix_with_non_canonical_keywords(self): + for config in ("match-all-and-more", "match-all-and-more-before"): + with raises(ConfigParseError): + load_config(config).lookup("whatever") + + def test_may_come_after_canonical(self, socket): + result = load_config("match-all-after-canonical").lookup("www") + assert result["user"] == "awesome" + + def test_may_not_come_before_canonical(self, socket): + with raises(ConfigParseError): + load_config("match-all-before-canonical") + + def test_after_canonical_not_loaded_when_non_canonicalized(self, socket): + result = load_config("match-canonical-no").lookup("a-host") + assert "user" not in result + + +def _expect(success_on): + """ + Returns a side_effect-friendly Invoke success result for given command(s). + + Ensures that any other commands fail; this is useful for testing 'Match + exec' because it means all other such clauses under test act like no-ops. + + :param success_on: + Single string or list of strings, noting commands that should appear to + succeed. + """ + if isinstance(success_on, string_types): + success_on = [success_on] + + def inner(command, *args, **kwargs): + # Sanity checking - we always expect that invoke.run is called with + # these. + assert kwargs.get("hide", None) == "stdout" + assert kwargs.get("warn", None) is True + # Fake exit + exit = 0 if command in success_on else 1 + return Result(exited=exit) + return inner + + +class TestMatchExec(object): + @patch("paramiko.config.invoke.run") + @mark.parametrize( + "cmd,user", + [ + ("unquoted", "rando"), + ("quoted", "benjamin"), + ("quoted spaced", "neil"), + ], + ) + def test_accepts_single_possibly_quoted_argument(self, run, cmd, user): + run.side_effect = _expect(cmd) + result = load_config("match-exec").lookup("whatever") + assert result["user"] == user + + @patch("paramiko.config.invoke.run") + def test_does_not_match_nonzero_exit_codes(self, run): + # Nothing will succeed -> no User ever gets loaded + run.return_value = Result(exited=1) + result = load_config("match-exec").lookup("whatever") + assert "user" not in result + + def test_tokenizes_argument(self): + # TODO: spot check a few common ones like %h, %p, %l? + assert False + + def test_works_with_canonical(self, socket): + # TODO: before AND after. same file, different key/values, prove both + # show up? + assert False + + def test_may_be_negated(self): + assert False + + def test_requires_an_argument(self): + assert False + + +class TestMatchHost(object): + def test_matches_target_name_when_no_hostname(self): + result = load_config("match-host").lookup("target") + assert result["user"] == "rand" + + def test_matches_hostname_from_global_setting(self): + # Also works for ones set in regular Host stanzas + result = load_config("match-host-name").lookup("anything") + assert result["user"] == "silly" + + def test_matches_hostname_from_earlier_match(self): + # Corner case: one Match matches original host, sets HostName, + # subsequent Match matches the latter. + result = load_config("match-host-from-match").lookup("original-host") + assert result["user"] == "inner" + + def test_may_be_globbed(self): + result = load_config("match-host-glob-list").lookup("whatever") + assert result["user"] == "matrim" + + def test_may_be_comma_separated_list(self): + for target in ("somehost", "someotherhost"): + result = load_config("match-host-glob-list").lookup(target) + assert result["user"] == "thom" + + def test_comma_separated_list_may_have_internal_negation(self): + conf = load_config("match-host-glob-list") + assert conf.lookup("good")["user"] == "perrin" + assert "user" not in conf.lookup("goof") + + def test_matches_canonicalized_name(self, socket): + # Without 'canonical' explicitly declared, mind. + result = load_config("match-host-canonicalized").lookup("www") + assert result["user"] == "rand" + + def test_works_with_canonical_keyword(self, socket): + # NOTE: distinct from 'happens to be canonicalized' above + # TODO: before AND after. same file, different key/values, prove both + # show up? + result = load_config("match-host-canonicalized").lookup("docs") + assert result["user"] == "eric" + + def test_may_be_negated(self): + conf = load_config("match-host-negated") + assert conf.lookup("docs")["user"] == "jeff" + assert "user" not in conf.lookup("www") + + def test_requires_an_argument(self): + with raises(ConfigParseError): + load_config("match-host-no-arg") + + +class TestMatchOriginalHost(object): + def test_matches_target_host_not_hostname(self): + result = load_config("match-orighost").lookup("target") + assert result["hostname"] == "bogus" + assert result["user"] == "tuon" + + def test_matches_target_host_not_canonicalized_name(self, socket): + result = load_config("match-orighost-canonical").lookup("www") + assert result["hostname"] == "www.paramiko.org" + assert result["user"] == "tuon" + + def test_may_be_globbed(self): + result = load_config("match-orighost").lookup("whatever") + assert result["user"] == "matrim" + + def test_may_be_comma_separated_list(self): + for target in ("comma", "separated"): + result = load_config("match-orighost").lookup(target) + assert result["user"] == "chameleon" + + def test_comma_separated_list_may_have_internal_negation(self): + result = load_config("match-orighost").lookup("nope") + assert "user" not in result + + def test_may_be_negated(self): + result = load_config("match-orighost").lookup("docs") + assert result["user"] == "thom" + + def test_requires_an_argument(self): + with raises(ConfigParseError): + load_config("match-orighost-no-arg") + + +class TestMatchUser(object): + def test_matches_configured_username(self): + result = load_config("match-user-explicit").lookup("anything") + assert result["hostname"] == "dumb" + + @patch("paramiko.config.getpass.getuser") + def test_matches_local_username_by_default(self, getuser): + getuser.return_value = "gandalf" + result = load_config("match-user").lookup("anything") + assert result["hostname"] == "gondor" + + @patch("paramiko.config.getpass.getuser") + def test_may_be_globbed(self, getuser): + for user in ("bilbo", "bombadil"): + getuser.return_value = user + result = load_config("match-user").lookup("anything") + assert result["hostname"] == "shire" + + @patch("paramiko.config.getpass.getuser") + def test_may_be_comma_separated_list(self, getuser): + for user in ("aragorn", "frodo"): + getuser.return_value = user + result = load_config("match-user").lookup("anything") + assert result["hostname"] == "moria" + + @patch("paramiko.config.getpass.getuser") + def test_comma_separated_list_may_have_internal_negation(self, getuser): + getuser.return_value = "legolas" + result = load_config("match-user").lookup("anything") + assert "port" not in result + getuser.return_value = "gimli" + result = load_config("match-user").lookup("anything") + assert result["port"] == "7373" + + @patch("paramiko.config.getpass.getuser") + def test_may_be_negated(self, getuser): + getuser.return_value = "saruman" + result = load_config("match-user").lookup("anything") + assert result["hostname"] == "mordor" + + def test_requires_an_argument(self): + with raises(ConfigParseError): + load_config("match-user-no-arg") + + +# NOTE: highly derivative of previous suite due to the former's use of +# localuser fallback. Doesn't seem worth conflating/refactoring right now. +class TestMatchLocalUser(object): + @patch("paramiko.config.getpass.getuser") + def test_matches_local_username(self, getuser): + getuser.return_value = "gandalf" + result = load_config("match-localuser").lookup("anything") + assert result["hostname"] == "gondor" + + @patch("paramiko.config.getpass.getuser") + def test_may_be_globbed(self, getuser): + for user in ("bilbo", "bombadil"): + getuser.return_value = user + result = load_config("match-localuser").lookup("anything") + assert result["hostname"] == "shire" + + @patch("paramiko.config.getpass.getuser") + def test_may_be_comma_separated_list(self, getuser): + for user in ("aragorn", "frodo"): + getuser.return_value = user + result = load_config("match-localuser").lookup("anything") + assert result["hostname"] == "moria" + + @patch("paramiko.config.getpass.getuser") + def test_comma_separated_list_may_have_internal_negation(self, getuser): + getuser.return_value = "legolas" + result = load_config("match-localuser").lookup("anything") + assert "port" not in result + getuser.return_value = "gimli" + result = load_config("match-localuser").lookup("anything") + assert result["port"] == "7373" + + @patch("paramiko.config.getpass.getuser") + def test_may_be_negated(self, getuser): + getuser.return_value = "saruman" + result = load_config("match-localuser").lookup("anything") + assert result["hostname"] == "mordor" + + def test_requires_an_argument(self): + with raises(ConfigParseError): + load_config("match-localuser-no-arg") + + +class TestComplexMatching(object): + # NOTE: this is still a cherry-pick of a few levels of complexity, there's + # no point testing literally all possible combinations. + + def test_canonical_exec(self, socket): + assert False + + def test_originalhost_host(self): + result = load_config("match-complex").lookup("target") + assert result["hostname"] == "bogus" + assert result["user"] == "rand" + + @patch("paramiko.config.getpass.getuser") + def test_originalhost_localuser(self, getuser): + getuser.return_value = "rando" + result = load_config("match-complex").lookup("remote") + assert result["user"] == "calrissian" + + @patch("paramiko.config.getpass.getuser") + def test_everything_but_all(self, getuser): + getuser.return_value = "rando" + result = load_config("match-complex").lookup("www") + assert result["port"] == "7777" + + @patch("paramiko.config.getpass.getuser") + def test_everything_but_all_with_some_negated(self, getuser): + getuser.return_value = "rando" + result = load_config("match-complex").lookup("docs") + assert result["port"] == "1234" + + def test_negated_canonical(self, socket): + # !canonical in a config that is not canonicalized - does match + result = load_config("match-canonical-no").lookup("specific") + assert result["user"] == "overload" + # !canonical in a config that is canonicalized - does NOT match + result = load_config("match-canonical-yes").lookup("www") + assert result["user"] == "hidden" diff --git a/tests/test_util.py b/tests/test_util.py index 84a48bd3..8ce260d1 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -56,6 +56,7 @@ class UtilTest(unittest.TestCase): "BufferedFile", "Channel", "ChannelException", + "ConfigParseError", "CouldNotCanonicalize", "DSSKey", "HostKeys", diff --git a/tests/util.py b/tests/util.py index 339677aa..9057f516 100644 --- a/tests/util.py +++ b/tests/util.py @@ -17,7 +17,7 @@ def _support(filename): def _config(name): - return join(tests_dir, "configs", "{}.config".format(name)) + return join(tests_dir, "configs", name) needs_gssapi = pytest.mark.skipif( |