test_netrc.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. import netrc, os, unittest, sys, textwrap
  2. from test.support import os_helper, run_unittest
  3. try:
  4. import pwd
  5. except ImportError:
  6. pwd = None
  7. temp_filename = os_helper.TESTFN
  8. class NetrcTestCase(unittest.TestCase):
  9. def make_nrc(self, test_data):
  10. test_data = textwrap.dedent(test_data)
  11. mode = 'w'
  12. if sys.platform != 'cygwin':
  13. mode += 't'
  14. with open(temp_filename, mode, encoding="utf-8") as fp:
  15. fp.write(test_data)
  16. try:
  17. nrc = netrc.netrc(temp_filename)
  18. finally:
  19. os.unlink(temp_filename)
  20. return nrc
  21. def test_toplevel_non_ordered_tokens(self):
  22. nrc = self.make_nrc("""\
  23. machine host.domain.com password pass1 login log1 account acct1
  24. default login log2 password pass2 account acct2
  25. """)
  26. self.assertEqual(nrc.hosts['host.domain.com'], ('log1', 'acct1', 'pass1'))
  27. self.assertEqual(nrc.hosts['default'], ('log2', 'acct2', 'pass2'))
  28. def test_toplevel_tokens(self):
  29. nrc = self.make_nrc("""\
  30. machine host.domain.com login log1 password pass1 account acct1
  31. default login log2 password pass2 account acct2
  32. """)
  33. self.assertEqual(nrc.hosts['host.domain.com'], ('log1', 'acct1', 'pass1'))
  34. self.assertEqual(nrc.hosts['default'], ('log2', 'acct2', 'pass2'))
  35. def test_macros(self):
  36. data = """\
  37. macdef macro1
  38. line1
  39. line2
  40. macdef macro2
  41. line3
  42. line4
  43. """
  44. nrc = self.make_nrc(data)
  45. self.assertEqual(nrc.macros, {'macro1': ['line1\n', 'line2\n'],
  46. 'macro2': ['line3\n', 'line4\n']})
  47. # strip the last \n
  48. self.assertRaises(netrc.NetrcParseError, self.make_nrc,
  49. data.rstrip(' ')[:-1])
  50. def test_optional_tokens(self):
  51. data = (
  52. "machine host.domain.com",
  53. "machine host.domain.com login",
  54. "machine host.domain.com account",
  55. "machine host.domain.com password",
  56. "machine host.domain.com login \"\" account",
  57. "machine host.domain.com login \"\" password",
  58. "machine host.domain.com account \"\" password"
  59. )
  60. for item in data:
  61. nrc = self.make_nrc(item)
  62. self.assertEqual(nrc.hosts['host.domain.com'], ('', '', ''))
  63. data = (
  64. "default",
  65. "default login",
  66. "default account",
  67. "default password",
  68. "default login \"\" account",
  69. "default login \"\" password",
  70. "default account \"\" password"
  71. )
  72. for item in data:
  73. nrc = self.make_nrc(item)
  74. self.assertEqual(nrc.hosts['default'], ('', '', ''))
  75. def test_invalid_tokens(self):
  76. data = (
  77. "invalid host.domain.com",
  78. "machine host.domain.com invalid",
  79. "machine host.domain.com login log password pass account acct invalid",
  80. "default host.domain.com invalid",
  81. "default host.domain.com login log password pass account acct invalid"
  82. )
  83. for item in data:
  84. self.assertRaises(netrc.NetrcParseError, self.make_nrc, item)
  85. def _test_token_x(self, nrc, token, value):
  86. nrc = self.make_nrc(nrc)
  87. if token == 'login':
  88. self.assertEqual(nrc.hosts['host.domain.com'], (value, 'acct', 'pass'))
  89. elif token == 'account':
  90. self.assertEqual(nrc.hosts['host.domain.com'], ('log', value, 'pass'))
  91. elif token == 'password':
  92. self.assertEqual(nrc.hosts['host.domain.com'], ('log', 'acct', value))
  93. def test_token_value_quotes(self):
  94. self._test_token_x("""\
  95. machine host.domain.com login "log" password pass account acct
  96. """, 'login', 'log')
  97. self._test_token_x("""\
  98. machine host.domain.com login log password pass account "acct"
  99. """, 'account', 'acct')
  100. self._test_token_x("""\
  101. machine host.domain.com login log password "pass" account acct
  102. """, 'password', 'pass')
  103. def test_token_value_escape(self):
  104. self._test_token_x("""\
  105. machine host.domain.com login \\"log password pass account acct
  106. """, 'login', '"log')
  107. self._test_token_x("""\
  108. machine host.domain.com login "\\"log" password pass account acct
  109. """, 'login', '"log')
  110. self._test_token_x("""\
  111. machine host.domain.com login log password pass account \\"acct
  112. """, 'account', '"acct')
  113. self._test_token_x("""\
  114. machine host.domain.com login log password pass account "\\"acct"
  115. """, 'account', '"acct')
  116. self._test_token_x("""\
  117. machine host.domain.com login log password \\"pass account acct
  118. """, 'password', '"pass')
  119. self._test_token_x("""\
  120. machine host.domain.com login log password "\\"pass" account acct
  121. """, 'password', '"pass')
  122. def test_token_value_whitespace(self):
  123. self._test_token_x("""\
  124. machine host.domain.com login "lo g" password pass account acct
  125. """, 'login', 'lo g')
  126. self._test_token_x("""\
  127. machine host.domain.com login log password "pas s" account acct
  128. """, 'password', 'pas s')
  129. self._test_token_x("""\
  130. machine host.domain.com login log password pass account "acc t"
  131. """, 'account', 'acc t')
  132. def test_token_value_non_ascii(self):
  133. self._test_token_x("""\
  134. machine host.domain.com login \xa1\xa2 password pass account acct
  135. """, 'login', '\xa1\xa2')
  136. self._test_token_x("""\
  137. machine host.domain.com login log password pass account \xa1\xa2
  138. """, 'account', '\xa1\xa2')
  139. self._test_token_x("""\
  140. machine host.domain.com login log password \xa1\xa2 account acct
  141. """, 'password', '\xa1\xa2')
  142. def test_token_value_leading_hash(self):
  143. self._test_token_x("""\
  144. machine host.domain.com login #log password pass account acct
  145. """, 'login', '#log')
  146. self._test_token_x("""\
  147. machine host.domain.com login log password pass account #acct
  148. """, 'account', '#acct')
  149. self._test_token_x("""\
  150. machine host.domain.com login log password #pass account acct
  151. """, 'password', '#pass')
  152. def test_token_value_trailing_hash(self):
  153. self._test_token_x("""\
  154. machine host.domain.com login log# password pass account acct
  155. """, 'login', 'log#')
  156. self._test_token_x("""\
  157. machine host.domain.com login log password pass account acct#
  158. """, 'account', 'acct#')
  159. self._test_token_x("""\
  160. machine host.domain.com login log password pass# account acct
  161. """, 'password', 'pass#')
  162. def test_token_value_internal_hash(self):
  163. self._test_token_x("""\
  164. machine host.domain.com login lo#g password pass account acct
  165. """, 'login', 'lo#g')
  166. self._test_token_x("""\
  167. machine host.domain.com login log password pass account ac#ct
  168. """, 'account', 'ac#ct')
  169. self._test_token_x("""\
  170. machine host.domain.com login log password pa#ss account acct
  171. """, 'password', 'pa#ss')
  172. def _test_comment(self, nrc, passwd='pass'):
  173. nrc = self.make_nrc(nrc)
  174. self.assertEqual(nrc.hosts['foo.domain.com'], ('bar', '', passwd))
  175. self.assertEqual(nrc.hosts['bar.domain.com'], ('foo', '', 'pass'))
  176. def test_comment_before_machine_line(self):
  177. self._test_comment("""\
  178. # comment
  179. machine foo.domain.com login bar password pass
  180. machine bar.domain.com login foo password pass
  181. """)
  182. def test_comment_before_machine_line_no_space(self):
  183. self._test_comment("""\
  184. #comment
  185. machine foo.domain.com login bar password pass
  186. machine bar.domain.com login foo password pass
  187. """)
  188. def test_comment_before_machine_line_hash_only(self):
  189. self._test_comment("""\
  190. #
  191. machine foo.domain.com login bar password pass
  192. machine bar.domain.com login foo password pass
  193. """)
  194. def test_comment_after_machine_line(self):
  195. self._test_comment("""\
  196. machine foo.domain.com login bar password pass
  197. # comment
  198. machine bar.domain.com login foo password pass
  199. """)
  200. self._test_comment("""\
  201. machine foo.domain.com login bar password pass
  202. machine bar.domain.com login foo password pass
  203. # comment
  204. """)
  205. def test_comment_after_machine_line_no_space(self):
  206. self._test_comment("""\
  207. machine foo.domain.com login bar password pass
  208. #comment
  209. machine bar.domain.com login foo password pass
  210. """)
  211. self._test_comment("""\
  212. machine foo.domain.com login bar password pass
  213. machine bar.domain.com login foo password pass
  214. #comment
  215. """)
  216. def test_comment_after_machine_line_hash_only(self):
  217. self._test_comment("""\
  218. machine foo.domain.com login bar password pass
  219. #
  220. machine bar.domain.com login foo password pass
  221. """)
  222. self._test_comment("""\
  223. machine foo.domain.com login bar password pass
  224. machine bar.domain.com login foo password pass
  225. #
  226. """)
  227. def test_comment_at_end_of_machine_line(self):
  228. self._test_comment("""\
  229. machine foo.domain.com login bar password pass # comment
  230. machine bar.domain.com login foo password pass
  231. """)
  232. def test_comment_at_end_of_machine_line_no_space(self):
  233. self._test_comment("""\
  234. machine foo.domain.com login bar password pass #comment
  235. machine bar.domain.com login foo password pass
  236. """)
  237. def test_comment_at_end_of_machine_line_pass_has_hash(self):
  238. self._test_comment("""\
  239. machine foo.domain.com login bar password #pass #comment
  240. machine bar.domain.com login foo password pass
  241. """, '#pass')
  242. @unittest.skipUnless(os.name == 'posix', 'POSIX only test')
  243. @unittest.skipIf(pwd is None, 'security check requires pwd module')
  244. @os_helper.skip_unless_working_chmod
  245. def test_security(self):
  246. # This test is incomplete since we are normally not run as root and
  247. # therefore can't test the file ownership being wrong.
  248. d = os_helper.TESTFN
  249. os.mkdir(d)
  250. self.addCleanup(os_helper.rmtree, d)
  251. fn = os.path.join(d, '.netrc')
  252. with open(fn, 'wt') as f:
  253. f.write("""\
  254. machine foo.domain.com login bar password pass
  255. default login foo password pass
  256. """)
  257. with os_helper.EnvironmentVarGuard() as environ:
  258. environ.set('HOME', d)
  259. os.chmod(fn, 0o600)
  260. nrc = netrc.netrc()
  261. self.assertEqual(nrc.hosts['foo.domain.com'],
  262. ('bar', '', 'pass'))
  263. os.chmod(fn, 0o622)
  264. self.assertRaises(netrc.NetrcParseError, netrc.netrc)
  265. with open(fn, 'wt') as f:
  266. f.write("""\
  267. machine foo.domain.com login anonymous password pass
  268. default login foo password pass
  269. """)
  270. with os_helper.EnvironmentVarGuard() as environ:
  271. environ.set('HOME', d)
  272. os.chmod(fn, 0o600)
  273. nrc = netrc.netrc()
  274. self.assertEqual(nrc.hosts['foo.domain.com'],
  275. ('anonymous', '', 'pass'))
  276. os.chmod(fn, 0o622)
  277. self.assertEqual(nrc.hosts['foo.domain.com'],
  278. ('anonymous', '', 'pass'))
  279. def test_main():
  280. run_unittest(NetrcTestCase)
  281. if __name__ == "__main__":
  282. test_main()