You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

652 line
25 KiB

  1. """tests for passlib.apache -- (c) Assurance Technologies 2008-2011"""
  2. #=============================================================================
  3. # imports
  4. #=============================================================================
  5. from __future__ import with_statement
  6. # core
  7. from logging import getLogger
  8. import os
  9. # site
  10. # pkg
  11. from passlib import apache
  12. from passlib.exc import MissingBackendError
  13. from passlib.utils.compat import irange
  14. from passlib.tests.utils import TestCase, get_file, set_file, ensure_mtime_changed
  15. from passlib.utils.compat import u
  16. from passlib.utils import to_bytes
  17. # module
  18. log = getLogger(__name__)
  19. def backdate_file_mtime(path, offset=10):
  20. """backdate file's mtime by specified amount"""
  21. # NOTE: this is used so we can test code which detects mtime changes,
  22. # without having to actually *pause* for that long.
  23. atime = os.path.getatime(path)
  24. mtime = os.path.getmtime(path)-offset
  25. os.utime(path, (atime, mtime))
  26. #=============================================================================
  27. # htpasswd
  28. #=============================================================================
  29. class HtpasswdFileTest(TestCase):
  30. """test HtpasswdFile class"""
  31. descriptionPrefix = "HtpasswdFile"
  32. # sample with 4 users
  33. sample_01 = (b'user2:2CHkkwa2AtqGs\n'
  34. b'user3:{SHA}3ipNV1GrBtxPmHFC21fCbVCSXIo=\n'
  35. b'user4:pass4\n'
  36. b'user1:$apr1$t4tc7jTh$GPIWVUo8sQKJlUdV8V5vu0\n')
  37. # sample 1 with user 1, 2 deleted; 4 changed
  38. sample_02 = b'user3:{SHA}3ipNV1GrBtxPmHFC21fCbVCSXIo=\nuser4:pass4\n'
  39. # sample 1 with user2 updated, user 1 first entry removed, and user 5 added
  40. sample_03 = (b'user2:pass2x\n'
  41. b'user3:{SHA}3ipNV1GrBtxPmHFC21fCbVCSXIo=\n'
  42. b'user4:pass4\n'
  43. b'user1:$apr1$t4tc7jTh$GPIWVUo8sQKJlUdV8V5vu0\n'
  44. b'user5:pass5\n')
  45. # standalone sample with 8-bit username
  46. sample_04_utf8 = b'user\xc3\xa6:2CHkkwa2AtqGs\n'
  47. sample_04_latin1 = b'user\xe6:2CHkkwa2AtqGs\n'
  48. sample_dup = b'user1:pass1\nuser1:pass2\n'
  49. # sample with bcrypt & sha256_crypt hashes
  50. sample_05 = (b'user2:2CHkkwa2AtqGs\n'
  51. b'user3:{SHA}3ipNV1GrBtxPmHFC21fCbVCSXIo=\n'
  52. b'user4:pass4\n'
  53. b'user1:$apr1$t4tc7jTh$GPIWVUo8sQKJlUdV8V5vu0\n'
  54. b'user5:$2a$12$yktDxraxijBZ360orOyCOePFGhuis/umyPNJoL5EbsLk.s6SWdrRO\n'
  55. b'user6:$5$rounds=110000$cCRp/xUUGVgwR4aP$'
  56. b'p0.QKFS5qLNRqw1/47lXYiAcgIjJK.WjCO8nrEKuUK.\n')
  57. def test_00_constructor_autoload(self):
  58. """test constructor autoload"""
  59. # check with existing file
  60. path = self.mktemp()
  61. set_file(path, self.sample_01)
  62. ht = apache.HtpasswdFile(path)
  63. self.assertEqual(ht.to_string(), self.sample_01)
  64. self.assertEqual(ht.path, path)
  65. self.assertTrue(ht.mtime)
  66. # check changing path
  67. ht.path = path + "x"
  68. self.assertEqual(ht.path, path + "x")
  69. self.assertFalse(ht.mtime)
  70. # check new=True
  71. ht = apache.HtpasswdFile(path, new=True)
  72. self.assertEqual(ht.to_string(), b"")
  73. self.assertEqual(ht.path, path)
  74. self.assertFalse(ht.mtime)
  75. # check autoload=False (deprecated alias for new=True)
  76. with self.assertWarningList("``autoload=False`` is deprecated"):
  77. ht = apache.HtpasswdFile(path, autoload=False)
  78. self.assertEqual(ht.to_string(), b"")
  79. self.assertEqual(ht.path, path)
  80. self.assertFalse(ht.mtime)
  81. # check missing file
  82. os.remove(path)
  83. self.assertRaises(IOError, apache.HtpasswdFile, path)
  84. # NOTE: "default_scheme" option checked via set_password() test, among others
  85. def test_00_from_path(self):
  86. path = self.mktemp()
  87. set_file(path, self.sample_01)
  88. ht = apache.HtpasswdFile.from_path(path)
  89. self.assertEqual(ht.to_string(), self.sample_01)
  90. self.assertEqual(ht.path, None)
  91. self.assertFalse(ht.mtime)
  92. def test_01_delete(self):
  93. """test delete()"""
  94. ht = apache.HtpasswdFile.from_string(self.sample_01)
  95. self.assertTrue(ht.delete("user1")) # should delete both entries
  96. self.assertTrue(ht.delete("user2"))
  97. self.assertFalse(ht.delete("user5")) # user not present
  98. self.assertEqual(ht.to_string(), self.sample_02)
  99. # invalid user
  100. self.assertRaises(ValueError, ht.delete, "user:")
  101. def test_01_delete_autosave(self):
  102. path = self.mktemp()
  103. sample = b'user1:pass1\nuser2:pass2\n'
  104. set_file(path, sample)
  105. ht = apache.HtpasswdFile(path)
  106. ht.delete("user1")
  107. self.assertEqual(get_file(path), sample)
  108. ht = apache.HtpasswdFile(path, autosave=True)
  109. ht.delete("user1")
  110. self.assertEqual(get_file(path), b"user2:pass2\n")
  111. def test_02_set_password(self):
  112. """test set_password()"""
  113. ht = apache.HtpasswdFile.from_string(
  114. self.sample_01, default_scheme="plaintext")
  115. self.assertTrue(ht.set_password("user2", "pass2x"))
  116. self.assertFalse(ht.set_password("user5", "pass5"))
  117. self.assertEqual(ht.to_string(), self.sample_03)
  118. # test legacy default kwd
  119. with self.assertWarningList("``default`` is deprecated"):
  120. ht = apache.HtpasswdFile.from_string(self.sample_01, default="plaintext")
  121. self.assertTrue(ht.set_password("user2", "pass2x"))
  122. self.assertFalse(ht.set_password("user5", "pass5"))
  123. self.assertEqual(ht.to_string(), self.sample_03)
  124. # invalid user
  125. self.assertRaises(ValueError, ht.set_password, "user:", "pass")
  126. # test that legacy update() still works
  127. with self.assertWarningList("update\(\) is deprecated"):
  128. ht.update("user2", "test")
  129. self.assertTrue(ht.check_password("user2", "test"))
  130. def test_02_set_password_autosave(self):
  131. path = self.mktemp()
  132. sample = b'user1:pass1\n'
  133. set_file(path, sample)
  134. ht = apache.HtpasswdFile(path)
  135. ht.set_password("user1", "pass2")
  136. self.assertEqual(get_file(path), sample)
  137. ht = apache.HtpasswdFile(path, default_scheme="plaintext", autosave=True)
  138. ht.set_password("user1", "pass2")
  139. self.assertEqual(get_file(path), b"user1:pass2\n")
  140. def test_02_set_password_default_scheme(self):
  141. """test set_password() -- default_scheme"""
  142. def check(scheme):
  143. ht = apache.HtpasswdFile(default_scheme=scheme)
  144. ht.set_password("user1", "pass1")
  145. return ht.context.identify(ht.get_hash("user1"))
  146. # explicit scheme
  147. self.assertEqual(check("sha256_crypt"), "sha256_crypt")
  148. self.assertEqual(check("des_crypt"), "des_crypt")
  149. # unknown scheme
  150. self.assertRaises(KeyError, check, "xxx")
  151. # alias resolution
  152. self.assertEqual(check("portable"), apache.htpasswd_defaults["portable"])
  153. self.assertEqual(check("portable_apache_22"), apache.htpasswd_defaults["portable_apache_22"])
  154. self.assertEqual(check("host_apache_22"), apache.htpasswd_defaults["host_apache_22"])
  155. # default
  156. self.assertEqual(check(None), apache.htpasswd_defaults["portable_apache_22"])
  157. def test_03_users(self):
  158. """test users()"""
  159. ht = apache.HtpasswdFile.from_string(self.sample_01)
  160. ht.set_password("user5", "pass5")
  161. ht.delete("user3")
  162. ht.set_password("user3", "pass3")
  163. self.assertEqual(sorted(ht.users()), ["user1", "user2", "user3", "user4", "user5"])
  164. def test_04_check_password(self):
  165. """test check_password()"""
  166. ht = apache.HtpasswdFile.from_string(self.sample_05)
  167. self.assertRaises(TypeError, ht.check_password, 1, 'pass9')
  168. self.assertTrue(ht.check_password("user9","pass9") is None)
  169. # users 1..6 of sample_01 run through all the main hash formats,
  170. # to make sure they're recognized.
  171. for i in irange(1, 7):
  172. i = str(i)
  173. try:
  174. self.assertTrue(ht.check_password("user"+i, "pass"+i))
  175. self.assertTrue(ht.check_password("user"+i, "pass9") is False)
  176. except MissingBackendError:
  177. if i == "5":
  178. # user5 uses bcrypt, which is apparently not available right now
  179. continue
  180. raise
  181. self.assertRaises(ValueError, ht.check_password, "user:", "pass")
  182. # test that legacy verify() still works
  183. with self.assertWarningList(["verify\(\) is deprecated"]*2):
  184. self.assertTrue(ht.verify("user1", "pass1"))
  185. self.assertFalse(ht.verify("user1", "pass2"))
  186. def test_05_load(self):
  187. """test load()"""
  188. # setup empty file
  189. path = self.mktemp()
  190. set_file(path, "")
  191. backdate_file_mtime(path, 5)
  192. ha = apache.HtpasswdFile(path, default_scheme="plaintext")
  193. self.assertEqual(ha.to_string(), b"")
  194. # make changes, check load_if_changed() does nothing
  195. ha.set_password("user1", "pass1")
  196. ha.load_if_changed()
  197. self.assertEqual(ha.to_string(), b"user1:pass1\n")
  198. # change file
  199. set_file(path, self.sample_01)
  200. ha.load_if_changed()
  201. self.assertEqual(ha.to_string(), self.sample_01)
  202. # make changes, check load() overwrites them
  203. ha.set_password("user5", "pass5")
  204. ha.load()
  205. self.assertEqual(ha.to_string(), self.sample_01)
  206. # test load w/ no path
  207. hb = apache.HtpasswdFile()
  208. self.assertRaises(RuntimeError, hb.load)
  209. self.assertRaises(RuntimeError, hb.load_if_changed)
  210. # test load w/ dups and explicit path
  211. set_file(path, self.sample_dup)
  212. hc = apache.HtpasswdFile()
  213. hc.load(path)
  214. self.assertTrue(hc.check_password('user1','pass1'))
  215. # NOTE: load_string() tested via from_string(), which is used all over this file
  216. def test_06_save(self):
  217. """test save()"""
  218. # load from file
  219. path = self.mktemp()
  220. set_file(path, self.sample_01)
  221. ht = apache.HtpasswdFile(path)
  222. # make changes, check they saved
  223. ht.delete("user1")
  224. ht.delete("user2")
  225. ht.save()
  226. self.assertEqual(get_file(path), self.sample_02)
  227. # test save w/ no path
  228. hb = apache.HtpasswdFile(default_scheme="plaintext")
  229. hb.set_password("user1", "pass1")
  230. self.assertRaises(RuntimeError, hb.save)
  231. # test save w/ explicit path
  232. hb.save(path)
  233. self.assertEqual(get_file(path), b"user1:pass1\n")
  234. def test_07_encodings(self):
  235. """test 'encoding' kwd"""
  236. # test bad encodings cause failure in constructor
  237. self.assertRaises(ValueError, apache.HtpasswdFile, encoding="utf-16")
  238. # check sample utf-8
  239. ht = apache.HtpasswdFile.from_string(self.sample_04_utf8, encoding="utf-8",
  240. return_unicode=True)
  241. self.assertEqual(ht.users(), [ u("user\u00e6") ])
  242. # test deprecated encoding=None
  243. with self.assertWarningList("``encoding=None`` is deprecated"):
  244. ht = apache.HtpasswdFile.from_string(self.sample_04_utf8, encoding=None)
  245. self.assertEqual(ht.users(), [ b'user\xc3\xa6' ])
  246. # check sample latin-1
  247. ht = apache.HtpasswdFile.from_string(self.sample_04_latin1,
  248. encoding="latin-1", return_unicode=True)
  249. self.assertEqual(ht.users(), [ u("user\u00e6") ])
  250. def test_08_get_hash(self):
  251. """test get_hash()"""
  252. ht = apache.HtpasswdFile.from_string(self.sample_01)
  253. self.assertEqual(ht.get_hash("user3"), b"{SHA}3ipNV1GrBtxPmHFC21fCbVCSXIo=")
  254. self.assertEqual(ht.get_hash("user4"), b"pass4")
  255. self.assertEqual(ht.get_hash("user5"), None)
  256. with self.assertWarningList("find\(\) is deprecated"):
  257. self.assertEqual(ht.find("user4"), b"pass4")
  258. def test_09_to_string(self):
  259. """test to_string"""
  260. # check with known sample
  261. ht = apache.HtpasswdFile.from_string(self.sample_01)
  262. self.assertEqual(ht.to_string(), self.sample_01)
  263. # test blank
  264. ht = apache.HtpasswdFile()
  265. self.assertEqual(ht.to_string(), b"")
  266. def test_10_repr(self):
  267. ht = apache.HtpasswdFile("fakepath", autosave=True, new=True, encoding="latin-1")
  268. repr(ht)
  269. def test_11_malformed(self):
  270. self.assertRaises(ValueError, apache.HtpasswdFile.from_string,
  271. b'realm:user1:pass1\n')
  272. self.assertRaises(ValueError, apache.HtpasswdFile.from_string,
  273. b'pass1\n')
  274. def test_12_from_string(self):
  275. # forbid path kwd
  276. self.assertRaises(TypeError, apache.HtpasswdFile.from_string,
  277. b'', path=None)
  278. def test_13_whitespace(self):
  279. """whitespace & comment handling"""
  280. # per htpasswd source (https://github.com/apache/httpd/blob/trunk/support/htpasswd.c),
  281. # lines that match "^\s*(#.*)?$" should be ignored
  282. source = to_bytes(
  283. '\n'
  284. 'user2:pass2\n'
  285. 'user4:pass4\n'
  286. 'user7:pass7\r\n'
  287. ' \t \n'
  288. 'user1:pass1\n'
  289. ' # legacy users\n'
  290. '#user6:pass6\n'
  291. 'user5:pass5\n\n'
  292. )
  293. # loading should see all users (except user6, who was commented out)
  294. ht = apache.HtpasswdFile.from_string(source)
  295. self.assertEqual(sorted(ht.users()), ["user1", "user2", "user4", "user5", "user7"])
  296. # update existing user
  297. ht.set_hash("user4", "althash4")
  298. self.assertEqual(sorted(ht.users()), ["user1", "user2", "user4", "user5", "user7"])
  299. # add a new user
  300. ht.set_hash("user6", "althash6")
  301. self.assertEqual(sorted(ht.users()), ["user1", "user2", "user4", "user5", "user6", "user7"])
  302. # delete existing user
  303. ht.delete("user7")
  304. self.assertEqual(sorted(ht.users()), ["user1", "user2", "user4", "user5", "user6"])
  305. # re-serialization should preserve whitespace
  306. target = to_bytes(
  307. '\n'
  308. 'user2:pass2\n'
  309. 'user4:althash4\n'
  310. ' \t \n'
  311. 'user1:pass1\n'
  312. ' # legacy users\n'
  313. '#user6:pass6\n'
  314. 'user5:pass5\n'
  315. 'user6:althash6\n'
  316. )
  317. self.assertEqual(ht.to_string(), target)
  318. #===================================================================
  319. # eoc
  320. #===================================================================
  321. #=============================================================================
  322. # htdigest
  323. #=============================================================================
  324. class HtdigestFileTest(TestCase):
  325. """test HtdigestFile class"""
  326. descriptionPrefix = "HtdigestFile"
  327. # sample with 4 users
  328. sample_01 = (b'user2:realm:549d2a5f4659ab39a80dac99e159ab19\n'
  329. b'user3:realm:a500bb8c02f6a9170ae46af10c898744\n'
  330. b'user4:realm:ab7b5d5f28ccc7666315f508c7358519\n'
  331. b'user1:realm:2a6cf53e7d8f8cf39d946dc880b14128\n')
  332. # sample 1 with user 1, 2 deleted; 4 changed
  333. sample_02 = (b'user3:realm:a500bb8c02f6a9170ae46af10c898744\n'
  334. b'user4:realm:ab7b5d5f28ccc7666315f508c7358519\n')
  335. # sample 1 with user2 updated, user 1 first entry removed, and user 5 added
  336. sample_03 = (b'user2:realm:5ba6d8328943c23c64b50f8b29566059\n'
  337. b'user3:realm:a500bb8c02f6a9170ae46af10c898744\n'
  338. b'user4:realm:ab7b5d5f28ccc7666315f508c7358519\n'
  339. b'user1:realm:2a6cf53e7d8f8cf39d946dc880b14128\n'
  340. b'user5:realm:03c55fdc6bf71552356ad401bdb9af19\n')
  341. # standalone sample with 8-bit username & realm
  342. sample_04_utf8 = b'user\xc3\xa6:realm\xc3\xa6:549d2a5f4659ab39a80dac99e159ab19\n'
  343. sample_04_latin1 = b'user\xe6:realm\xe6:549d2a5f4659ab39a80dac99e159ab19\n'
  344. def test_00_constructor_autoload(self):
  345. """test constructor autoload"""
  346. # check with existing file
  347. path = self.mktemp()
  348. set_file(path, self.sample_01)
  349. ht = apache.HtdigestFile(path)
  350. self.assertEqual(ht.to_string(), self.sample_01)
  351. # check without autoload
  352. ht = apache.HtdigestFile(path, new=True)
  353. self.assertEqual(ht.to_string(), b"")
  354. # check missing file
  355. os.remove(path)
  356. self.assertRaises(IOError, apache.HtdigestFile, path)
  357. # NOTE: default_realm option checked via other tests.
  358. def test_01_delete(self):
  359. """test delete()"""
  360. ht = apache.HtdigestFile.from_string(self.sample_01)
  361. self.assertTrue(ht.delete("user1", "realm"))
  362. self.assertTrue(ht.delete("user2", "realm"))
  363. self.assertFalse(ht.delete("user5", "realm"))
  364. self.assertFalse(ht.delete("user3", "realm5"))
  365. self.assertEqual(ht.to_string(), self.sample_02)
  366. # invalid user
  367. self.assertRaises(ValueError, ht.delete, "user:", "realm")
  368. # invalid realm
  369. self.assertRaises(ValueError, ht.delete, "user", "realm:")
  370. def test_01_delete_autosave(self):
  371. path = self.mktemp()
  372. set_file(path, self.sample_01)
  373. ht = apache.HtdigestFile(path)
  374. self.assertTrue(ht.delete("user1", "realm"))
  375. self.assertFalse(ht.delete("user3", "realm5"))
  376. self.assertFalse(ht.delete("user5", "realm"))
  377. self.assertEqual(get_file(path), self.sample_01)
  378. ht.autosave = True
  379. self.assertTrue(ht.delete("user2", "realm"))
  380. self.assertEqual(get_file(path), self.sample_02)
  381. def test_02_set_password(self):
  382. """test update()"""
  383. ht = apache.HtdigestFile.from_string(self.sample_01)
  384. self.assertTrue(ht.set_password("user2", "realm", "pass2x"))
  385. self.assertFalse(ht.set_password("user5", "realm", "pass5"))
  386. self.assertEqual(ht.to_string(), self.sample_03)
  387. # default realm
  388. self.assertRaises(TypeError, ht.set_password, "user2", "pass3")
  389. ht.default_realm = "realm2"
  390. ht.set_password("user2", "pass3")
  391. ht.check_password("user2", "realm2", "pass3")
  392. # invalid user
  393. self.assertRaises(ValueError, ht.set_password, "user:", "realm", "pass")
  394. self.assertRaises(ValueError, ht.set_password, "u"*256, "realm", "pass")
  395. # invalid realm
  396. self.assertRaises(ValueError, ht.set_password, "user", "realm:", "pass")
  397. self.assertRaises(ValueError, ht.set_password, "user", "r"*256, "pass")
  398. # test that legacy update() still works
  399. with self.assertWarningList("update\(\) is deprecated"):
  400. ht.update("user2", "realm2", "test")
  401. self.assertTrue(ht.check_password("user2", "test"))
  402. # TODO: test set_password autosave
  403. def test_03_users(self):
  404. """test users()"""
  405. ht = apache.HtdigestFile.from_string(self.sample_01)
  406. ht.set_password("user5", "realm", "pass5")
  407. ht.delete("user3", "realm")
  408. ht.set_password("user3", "realm", "pass3")
  409. self.assertEqual(sorted(ht.users("realm")), ["user1", "user2", "user3", "user4", "user5"])
  410. self.assertRaises(TypeError, ht.users, 1)
  411. def test_04_check_password(self):
  412. """test check_password()"""
  413. ht = apache.HtdigestFile.from_string(self.sample_01)
  414. self.assertRaises(TypeError, ht.check_password, 1, 'realm', 'pass5')
  415. self.assertRaises(TypeError, ht.check_password, 'user', 1, 'pass5')
  416. self.assertIs(ht.check_password("user5", "realm","pass5"), None)
  417. for i in irange(1,5):
  418. i = str(i)
  419. self.assertTrue(ht.check_password("user"+i, "realm", "pass"+i))
  420. self.assertIs(ht.check_password("user"+i, "realm", "pass5"), False)
  421. # default realm
  422. self.assertRaises(TypeError, ht.check_password, "user5", "pass5")
  423. ht.default_realm = "realm"
  424. self.assertTrue(ht.check_password("user1", "pass1"))
  425. self.assertIs(ht.check_password("user5", "pass5"), None)
  426. # test that legacy verify() still works
  427. with self.assertWarningList(["verify\(\) is deprecated"]*2):
  428. self.assertTrue(ht.verify("user1", "realm", "pass1"))
  429. self.assertFalse(ht.verify("user1", "realm", "pass2"))
  430. # invalid user
  431. self.assertRaises(ValueError, ht.check_password, "user:", "realm", "pass")
  432. def test_05_load(self):
  433. """test load()"""
  434. # setup empty file
  435. path = self.mktemp()
  436. set_file(path, "")
  437. backdate_file_mtime(path, 5)
  438. ha = apache.HtdigestFile(path)
  439. self.assertEqual(ha.to_string(), b"")
  440. # make changes, check load_if_changed() does nothing
  441. ha.set_password("user1", "realm", "pass1")
  442. ha.load_if_changed()
  443. self.assertEqual(ha.to_string(), b'user1:realm:2a6cf53e7d8f8cf39d946dc880b14128\n')
  444. # change file
  445. set_file(path, self.sample_01)
  446. ha.load_if_changed()
  447. self.assertEqual(ha.to_string(), self.sample_01)
  448. # make changes, check load_if_changed overwrites them
  449. ha.set_password("user5", "realm", "pass5")
  450. ha.load()
  451. self.assertEqual(ha.to_string(), self.sample_01)
  452. # test load w/ no path
  453. hb = apache.HtdigestFile()
  454. self.assertRaises(RuntimeError, hb.load)
  455. self.assertRaises(RuntimeError, hb.load_if_changed)
  456. # test load w/ explicit path
  457. hc = apache.HtdigestFile()
  458. hc.load(path)
  459. self.assertEqual(hc.to_string(), self.sample_01)
  460. # change file, test deprecated force=False kwd
  461. ensure_mtime_changed(path)
  462. set_file(path, "")
  463. with self.assertWarningList(r"load\(force=False\) is deprecated"):
  464. ha.load(force=False)
  465. self.assertEqual(ha.to_string(), b"")
  466. def test_06_save(self):
  467. """test save()"""
  468. # load from file
  469. path = self.mktemp()
  470. set_file(path, self.sample_01)
  471. ht = apache.HtdigestFile(path)
  472. # make changes, check they saved
  473. ht.delete("user1", "realm")
  474. ht.delete("user2", "realm")
  475. ht.save()
  476. self.assertEqual(get_file(path), self.sample_02)
  477. # test save w/ no path
  478. hb = apache.HtdigestFile()
  479. hb.set_password("user1", "realm", "pass1")
  480. self.assertRaises(RuntimeError, hb.save)
  481. # test save w/ explicit path
  482. hb.save(path)
  483. self.assertEqual(get_file(path), hb.to_string())
  484. def test_07_realms(self):
  485. """test realms() & delete_realm()"""
  486. ht = apache.HtdigestFile.from_string(self.sample_01)
  487. self.assertEqual(ht.delete_realm("x"), 0)
  488. self.assertEqual(ht.realms(), ['realm'])
  489. self.assertEqual(ht.delete_realm("realm"), 4)
  490. self.assertEqual(ht.realms(), [])
  491. self.assertEqual(ht.to_string(), b"")
  492. def test_08_get_hash(self):
  493. """test get_hash()"""
  494. ht = apache.HtdigestFile.from_string(self.sample_01)
  495. self.assertEqual(ht.get_hash("user3", "realm"), "a500bb8c02f6a9170ae46af10c898744")
  496. self.assertEqual(ht.get_hash("user4", "realm"), "ab7b5d5f28ccc7666315f508c7358519")
  497. self.assertEqual(ht.get_hash("user5", "realm"), None)
  498. with self.assertWarningList("find\(\) is deprecated"):
  499. self.assertEqual(ht.find("user4", "realm"), "ab7b5d5f28ccc7666315f508c7358519")
  500. def test_09_encodings(self):
  501. """test encoding parameter"""
  502. # test bad encodings cause failure in constructor
  503. self.assertRaises(ValueError, apache.HtdigestFile, encoding="utf-16")
  504. # check sample utf-8
  505. ht = apache.HtdigestFile.from_string(self.sample_04_utf8, encoding="utf-8", return_unicode=True)
  506. self.assertEqual(ht.realms(), [ u("realm\u00e6") ])
  507. self.assertEqual(ht.users(u("realm\u00e6")), [ u("user\u00e6") ])
  508. # check sample latin-1
  509. ht = apache.HtdigestFile.from_string(self.sample_04_latin1, encoding="latin-1", return_unicode=True)
  510. self.assertEqual(ht.realms(), [ u("realm\u00e6") ])
  511. self.assertEqual(ht.users(u("realm\u00e6")), [ u("user\u00e6") ])
  512. def test_10_to_string(self):
  513. """test to_string()"""
  514. # check sample
  515. ht = apache.HtdigestFile.from_string(self.sample_01)
  516. self.assertEqual(ht.to_string(), self.sample_01)
  517. # check blank
  518. ht = apache.HtdigestFile()
  519. self.assertEqual(ht.to_string(), b"")
  520. def test_11_malformed(self):
  521. self.assertRaises(ValueError, apache.HtdigestFile.from_string,
  522. b'realm:user1:pass1:other\n')
  523. self.assertRaises(ValueError, apache.HtdigestFile.from_string,
  524. b'user1:pass1\n')
  525. #===================================================================
  526. # eoc
  527. #===================================================================
  528. #=============================================================================
  529. # eof
  530. #=============================================================================