tests.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import datetime
  2. from django.core import signing
  3. from django.test import SimpleTestCase, override_settings
  4. from django.test.utils import freeze_time, ignore_warnings
  5. from django.utils.crypto import InvalidAlgorithm
  6. from django.utils.deprecation import RemovedInDjango51Warning
  7. class TestSigner(SimpleTestCase):
  8. def test_signature(self):
  9. "signature() method should generate a signature"
  10. signer = signing.Signer(key="predictable-secret")
  11. signer2 = signing.Signer(key="predictable-secret2")
  12. for s in (
  13. b"hello",
  14. b"3098247:529:087:",
  15. "\u2019".encode(),
  16. ):
  17. self.assertEqual(
  18. signer.signature(s),
  19. signing.base64_hmac(
  20. signer.salt + "signer",
  21. s,
  22. "predictable-secret",
  23. algorithm=signer.algorithm,
  24. ),
  25. )
  26. self.assertNotEqual(signer.signature(s), signer2.signature(s))
  27. def test_signature_with_salt(self):
  28. signer = signing.Signer(key="predictable-secret", salt="extra-salt")
  29. self.assertEqual(
  30. signer.signature("hello"),
  31. signing.base64_hmac(
  32. "extra-salt" + "signer",
  33. "hello",
  34. "predictable-secret",
  35. algorithm=signer.algorithm,
  36. ),
  37. )
  38. self.assertNotEqual(
  39. signing.Signer(key="predictable-secret", salt="one").signature("hello"),
  40. signing.Signer(key="predictable-secret", salt="two").signature("hello"),
  41. )
  42. def test_custom_algorithm(self):
  43. signer = signing.Signer(key="predictable-secret", algorithm="sha512")
  44. self.assertEqual(
  45. signer.signature("hello"),
  46. "Usf3uVQOZ9m6uPfVonKR-EBXjPe7bjMbp3_Fq8MfsptgkkM1ojidN0BxYaT5HAEN1"
  47. "VzO9_jVu7R-VkqknHYNvw",
  48. )
  49. def test_invalid_algorithm(self):
  50. signer = signing.Signer(key="predictable-secret", algorithm="whatever")
  51. msg = "'whatever' is not an algorithm accepted by the hashlib module."
  52. with self.assertRaisesMessage(InvalidAlgorithm, msg):
  53. signer.sign("hello")
  54. def test_sign_unsign(self):
  55. "sign/unsign should be reversible"
  56. signer = signing.Signer(key="predictable-secret")
  57. examples = [
  58. "q;wjmbk;wkmb",
  59. "3098247529087",
  60. "3098247:529:087:",
  61. "jkw osanteuh ,rcuh nthu aou oauh ,ud du",
  62. "\u2019",
  63. ]
  64. for example in examples:
  65. signed = signer.sign(example)
  66. self.assertIsInstance(signed, str)
  67. self.assertNotEqual(example, signed)
  68. self.assertEqual(example, signer.unsign(signed))
  69. def test_sign_unsign_non_string(self):
  70. signer = signing.Signer(key="predictable-secret")
  71. values = [
  72. 123,
  73. 1.23,
  74. True,
  75. datetime.date.today(),
  76. ]
  77. for value in values:
  78. with self.subTest(value):
  79. signed = signer.sign(value)
  80. self.assertIsInstance(signed, str)
  81. self.assertNotEqual(signed, value)
  82. self.assertEqual(signer.unsign(signed), str(value))
  83. def test_unsign_detects_tampering(self):
  84. "unsign should raise an exception if the value has been tampered with"
  85. signer = signing.Signer(key="predictable-secret")
  86. value = "Another string"
  87. signed_value = signer.sign(value)
  88. transforms = (
  89. lambda s: s.upper(),
  90. lambda s: s + "a",
  91. lambda s: "a" + s[1:],
  92. lambda s: s.replace(":", ""),
  93. )
  94. self.assertEqual(value, signer.unsign(signed_value))
  95. for transform in transforms:
  96. with self.assertRaises(signing.BadSignature):
  97. signer.unsign(transform(signed_value))
  98. def test_sign_unsign_object(self):
  99. signer = signing.Signer(key="predictable-secret")
  100. tests = [
  101. ["a", "list"],
  102. "a string \u2019",
  103. {"a": "dictionary"},
  104. ]
  105. for obj in tests:
  106. with self.subTest(obj=obj):
  107. signed_obj = signer.sign_object(obj)
  108. self.assertNotEqual(obj, signed_obj)
  109. self.assertEqual(obj, signer.unsign_object(signed_obj))
  110. signed_obj = signer.sign_object(obj, compress=True)
  111. self.assertNotEqual(obj, signed_obj)
  112. self.assertEqual(obj, signer.unsign_object(signed_obj))
  113. def test_dumps_loads(self):
  114. "dumps and loads be reversible for any JSON serializable object"
  115. objects = [
  116. ["a", "list"],
  117. "a string \u2019",
  118. {"a": "dictionary"},
  119. ]
  120. for o in objects:
  121. self.assertNotEqual(o, signing.dumps(o))
  122. self.assertEqual(o, signing.loads(signing.dumps(o)))
  123. self.assertNotEqual(o, signing.dumps(o, compress=True))
  124. self.assertEqual(o, signing.loads(signing.dumps(o, compress=True)))
  125. def test_decode_detects_tampering(self):
  126. "loads should raise exception for tampered objects"
  127. transforms = (
  128. lambda s: s.upper(),
  129. lambda s: s + "a",
  130. lambda s: "a" + s[1:],
  131. lambda s: s.replace(":", ""),
  132. )
  133. value = {
  134. "foo": "bar",
  135. "baz": 1,
  136. }
  137. encoded = signing.dumps(value)
  138. self.assertEqual(value, signing.loads(encoded))
  139. for transform in transforms:
  140. with self.assertRaises(signing.BadSignature):
  141. signing.loads(transform(encoded))
  142. def test_works_with_non_ascii_keys(self):
  143. binary_key = b"\xe7" # Set some binary (non-ASCII key)
  144. s = signing.Signer(key=binary_key)
  145. self.assertEqual(
  146. "foo:EE4qGC5MEKyQG5msxYA0sBohAxLC0BJf8uRhemh0BGU",
  147. s.sign("foo"),
  148. )
  149. def test_valid_sep(self):
  150. separators = ["/", "*sep*", ","]
  151. for sep in separators:
  152. signer = signing.Signer(key="predictable-secret", sep=sep)
  153. self.assertEqual(
  154. "foo%sjZQoX_FtSO70jX9HLRGg2A_2s4kdDBxz1QoO_OpEQb0" % sep,
  155. signer.sign("foo"),
  156. )
  157. def test_invalid_sep(self):
  158. """should warn on invalid separator"""
  159. msg = (
  160. "Unsafe Signer separator: %r (cannot be empty or consist of only A-z0-9-_=)"
  161. )
  162. separators = ["", "-", "abc"]
  163. for sep in separators:
  164. with self.assertRaisesMessage(ValueError, msg % sep):
  165. signing.Signer(sep=sep)
  166. def test_verify_with_non_default_key(self):
  167. old_signer = signing.Signer(key="secret")
  168. new_signer = signing.Signer(
  169. key="newsecret", fallback_keys=["othersecret", "secret"]
  170. )
  171. signed = old_signer.sign("abc")
  172. self.assertEqual(new_signer.unsign(signed), "abc")
  173. def test_sign_unsign_multiple_keys(self):
  174. """The default key is a valid verification key."""
  175. signer = signing.Signer(key="secret", fallback_keys=["oldsecret"])
  176. signed = signer.sign("abc")
  177. self.assertEqual(signer.unsign(signed), "abc")
  178. @override_settings(
  179. SECRET_KEY="secret",
  180. SECRET_KEY_FALLBACKS=["oldsecret"],
  181. )
  182. def test_sign_unsign_ignore_secret_key_fallbacks(self):
  183. old_signer = signing.Signer(key="oldsecret")
  184. signed = old_signer.sign("abc")
  185. signer = signing.Signer(fallback_keys=[])
  186. with self.assertRaises(signing.BadSignature):
  187. signer.unsign(signed)
  188. @override_settings(
  189. SECRET_KEY="secret",
  190. SECRET_KEY_FALLBACKS=["oldsecret"],
  191. )
  192. def test_default_keys_verification(self):
  193. old_signer = signing.Signer(key="oldsecret")
  194. signed = old_signer.sign("abc")
  195. signer = signing.Signer()
  196. self.assertEqual(signer.unsign(signed), "abc")
  197. class TestTimestampSigner(SimpleTestCase):
  198. def test_timestamp_signer(self):
  199. value = "hello"
  200. with freeze_time(123456789):
  201. signer = signing.TimestampSigner(key="predictable-key")
  202. ts = signer.sign(value)
  203. self.assertNotEqual(ts, signing.Signer(key="predictable-key").sign(value))
  204. self.assertEqual(signer.unsign(ts), value)
  205. with freeze_time(123456800):
  206. self.assertEqual(signer.unsign(ts, max_age=12), value)
  207. # max_age parameter can also accept a datetime.timedelta object
  208. self.assertEqual(
  209. signer.unsign(ts, max_age=datetime.timedelta(seconds=11)), value
  210. )
  211. with self.assertRaises(signing.SignatureExpired):
  212. signer.unsign(ts, max_age=10)
  213. class TestBase62(SimpleTestCase):
  214. def test_base62(self):
  215. tests = [-(10**10), 10**10, 1620378259, *range(-100, 100)]
  216. for i in tests:
  217. self.assertEqual(i, signing.b62_decode(signing.b62_encode(i)))
  218. class SignerPositionalArgumentsDeprecationTests(SimpleTestCase):
  219. def test_deprecation(self):
  220. msg = "Passing positional arguments to Signer is deprecated."
  221. with self.assertRaisesMessage(RemovedInDjango51Warning, msg):
  222. signing.Signer("predictable-secret")
  223. msg = "Passing positional arguments to TimestampSigner is deprecated."
  224. with self.assertRaisesMessage(RemovedInDjango51Warning, msg):
  225. signing.TimestampSigner("predictable-secret")
  226. @ignore_warnings(category=RemovedInDjango51Warning)
  227. def test_positional_arguments(self):
  228. signer = signing.Signer("secret", "/", "somesalt", "sha1", ["oldsecret"])
  229. signed = signer.sign("xyz")
  230. self.assertEqual(signed, "xyz/zzdO_8rk-NGnm8jNasXRTF2P5kY")
  231. self.assertEqual(signer.unsign(signed), "xyz")
  232. old_signer = signing.Signer("oldsecret", "/", "somesalt", "sha1")
  233. signed = old_signer.sign("xyz")
  234. self.assertEqual(signer.unsign(signed), "xyz")