123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- import datetime
- from django.core import signing
- from django.test import SimpleTestCase, override_settings
- from django.test.utils import freeze_time, ignore_warnings
- from django.utils.crypto import InvalidAlgorithm
- from django.utils.deprecation import RemovedInDjango51Warning
- class TestSigner(SimpleTestCase):
- def test_signature(self):
- "signature() method should generate a signature"
- signer = signing.Signer(key="predictable-secret")
- signer2 = signing.Signer(key="predictable-secret2")
- for s in (
- b"hello",
- b"3098247:529:087:",
- "\u2019".encode(),
- ):
- self.assertEqual(
- signer.signature(s),
- signing.base64_hmac(
- signer.salt + "signer",
- s,
- "predictable-secret",
- algorithm=signer.algorithm,
- ),
- )
- self.assertNotEqual(signer.signature(s), signer2.signature(s))
- def test_signature_with_salt(self):
- signer = signing.Signer(key="predictable-secret", salt="extra-salt")
- self.assertEqual(
- signer.signature("hello"),
- signing.base64_hmac(
- "extra-salt" + "signer",
- "hello",
- "predictable-secret",
- algorithm=signer.algorithm,
- ),
- )
- self.assertNotEqual(
- signing.Signer(key="predictable-secret", salt="one").signature("hello"),
- signing.Signer(key="predictable-secret", salt="two").signature("hello"),
- )
- def test_custom_algorithm(self):
- signer = signing.Signer(key="predictable-secret", algorithm="sha512")
- self.assertEqual(
- signer.signature("hello"),
- "Usf3uVQOZ9m6uPfVonKR-EBXjPe7bjMbp3_Fq8MfsptgkkM1ojidN0BxYaT5HAEN1"
- "VzO9_jVu7R-VkqknHYNvw",
- )
- def test_invalid_algorithm(self):
- signer = signing.Signer(key="predictable-secret", algorithm="whatever")
- msg = "'whatever' is not an algorithm accepted by the hashlib module."
- with self.assertRaisesMessage(InvalidAlgorithm, msg):
- signer.sign("hello")
- def test_sign_unsign(self):
- "sign/unsign should be reversible"
- signer = signing.Signer(key="predictable-secret")
- examples = [
- "q;wjmbk;wkmb",
- "3098247529087",
- "3098247:529:087:",
- "jkw osanteuh ,rcuh nthu aou oauh ,ud du",
- "\u2019",
- ]
- for example in examples:
- signed = signer.sign(example)
- self.assertIsInstance(signed, str)
- self.assertNotEqual(example, signed)
- self.assertEqual(example, signer.unsign(signed))
- def test_sign_unsign_non_string(self):
- signer = signing.Signer(key="predictable-secret")
- values = [
- 123,
- 1.23,
- True,
- datetime.date.today(),
- ]
- for value in values:
- with self.subTest(value):
- signed = signer.sign(value)
- self.assertIsInstance(signed, str)
- self.assertNotEqual(signed, value)
- self.assertEqual(signer.unsign(signed), str(value))
- def test_unsign_detects_tampering(self):
- "unsign should raise an exception if the value has been tampered with"
- signer = signing.Signer(key="predictable-secret")
- value = "Another string"
- signed_value = signer.sign(value)
- transforms = (
- lambda s: s.upper(),
- lambda s: s + "a",
- lambda s: "a" + s[1:],
- lambda s: s.replace(":", ""),
- )
- self.assertEqual(value, signer.unsign(signed_value))
- for transform in transforms:
- with self.assertRaises(signing.BadSignature):
- signer.unsign(transform(signed_value))
- def test_sign_unsign_object(self):
- signer = signing.Signer(key="predictable-secret")
- tests = [
- ["a", "list"],
- "a string \u2019",
- {"a": "dictionary"},
- ]
- for obj in tests:
- with self.subTest(obj=obj):
- signed_obj = signer.sign_object(obj)
- self.assertNotEqual(obj, signed_obj)
- self.assertEqual(obj, signer.unsign_object(signed_obj))
- signed_obj = signer.sign_object(obj, compress=True)
- self.assertNotEqual(obj, signed_obj)
- self.assertEqual(obj, signer.unsign_object(signed_obj))
- def test_dumps_loads(self):
- "dumps and loads be reversible for any JSON serializable object"
- objects = [
- ["a", "list"],
- "a string \u2019",
- {"a": "dictionary"},
- ]
- for o in objects:
- self.assertNotEqual(o, signing.dumps(o))
- self.assertEqual(o, signing.loads(signing.dumps(o)))
- self.assertNotEqual(o, signing.dumps(o, compress=True))
- self.assertEqual(o, signing.loads(signing.dumps(o, compress=True)))
- def test_decode_detects_tampering(self):
- "loads should raise exception for tampered objects"
- transforms = (
- lambda s: s.upper(),
- lambda s: s + "a",
- lambda s: "a" + s[1:],
- lambda s: s.replace(":", ""),
- )
- value = {
- "foo": "bar",
- "baz": 1,
- }
- encoded = signing.dumps(value)
- self.assertEqual(value, signing.loads(encoded))
- for transform in transforms:
- with self.assertRaises(signing.BadSignature):
- signing.loads(transform(encoded))
- def test_works_with_non_ascii_keys(self):
- binary_key = b"\xe7" # Set some binary (non-ASCII key)
- s = signing.Signer(key=binary_key)
- self.assertEqual(
- "foo:EE4qGC5MEKyQG5msxYA0sBohAxLC0BJf8uRhemh0BGU",
- s.sign("foo"),
- )
- def test_valid_sep(self):
- separators = ["/", "*sep*", ","]
- for sep in separators:
- signer = signing.Signer(key="predictable-secret", sep=sep)
- self.assertEqual(
- "foo%sjZQoX_FtSO70jX9HLRGg2A_2s4kdDBxz1QoO_OpEQb0" % sep,
- signer.sign("foo"),
- )
- def test_invalid_sep(self):
- """should warn on invalid separator"""
- msg = (
- "Unsafe Signer separator: %r (cannot be empty or consist of only A-z0-9-_=)"
- )
- separators = ["", "-", "abc"]
- for sep in separators:
- with self.assertRaisesMessage(ValueError, msg % sep):
- signing.Signer(sep=sep)
- def test_verify_with_non_default_key(self):
- old_signer = signing.Signer(key="secret")
- new_signer = signing.Signer(
- key="newsecret", fallback_keys=["othersecret", "secret"]
- )
- signed = old_signer.sign("abc")
- self.assertEqual(new_signer.unsign(signed), "abc")
- def test_sign_unsign_multiple_keys(self):
- """The default key is a valid verification key."""
- signer = signing.Signer(key="secret", fallback_keys=["oldsecret"])
- signed = signer.sign("abc")
- self.assertEqual(signer.unsign(signed), "abc")
- @override_settings(
- SECRET_KEY="secret",
- SECRET_KEY_FALLBACKS=["oldsecret"],
- )
- def test_sign_unsign_ignore_secret_key_fallbacks(self):
- old_signer = signing.Signer(key="oldsecret")
- signed = old_signer.sign("abc")
- signer = signing.Signer(fallback_keys=[])
- with self.assertRaises(signing.BadSignature):
- signer.unsign(signed)
- @override_settings(
- SECRET_KEY="secret",
- SECRET_KEY_FALLBACKS=["oldsecret"],
- )
- def test_default_keys_verification(self):
- old_signer = signing.Signer(key="oldsecret")
- signed = old_signer.sign("abc")
- signer = signing.Signer()
- self.assertEqual(signer.unsign(signed), "abc")
- class TestTimestampSigner(SimpleTestCase):
- def test_timestamp_signer(self):
- value = "hello"
- with freeze_time(123456789):
- signer = signing.TimestampSigner(key="predictable-key")
- ts = signer.sign(value)
- self.assertNotEqual(ts, signing.Signer(key="predictable-key").sign(value))
- self.assertEqual(signer.unsign(ts), value)
- with freeze_time(123456800):
- self.assertEqual(signer.unsign(ts, max_age=12), value)
- # max_age parameter can also accept a datetime.timedelta object
- self.assertEqual(
- signer.unsign(ts, max_age=datetime.timedelta(seconds=11)), value
- )
- with self.assertRaises(signing.SignatureExpired):
- signer.unsign(ts, max_age=10)
- class TestBase62(SimpleTestCase):
- def test_base62(self):
- tests = [-(10**10), 10**10, 1620378259, *range(-100, 100)]
- for i in tests:
- self.assertEqual(i, signing.b62_decode(signing.b62_encode(i)))
- class SignerPositionalArgumentsDeprecationTests(SimpleTestCase):
- def test_deprecation(self):
- msg = "Passing positional arguments to Signer is deprecated."
- with self.assertRaisesMessage(RemovedInDjango51Warning, msg):
- signing.Signer("predictable-secret")
- msg = "Passing positional arguments to TimestampSigner is deprecated."
- with self.assertRaisesMessage(RemovedInDjango51Warning, msg):
- signing.TimestampSigner("predictable-secret")
- @ignore_warnings(category=RemovedInDjango51Warning)
- def test_positional_arguments(self):
- signer = signing.Signer("secret", "/", "somesalt", "sha1", ["oldsecret"])
- signed = signer.sign("xyz")
- self.assertEqual(signed, "xyz/zzdO_8rk-NGnm8jNasXRTF2P5kY")
- self.assertEqual(signer.unsign(signed), "xyz")
- old_signer = signing.Signer("oldsecret", "/", "somesalt", "sha1")
- signed = old_signer.sign("xyz")
- self.assertEqual(signer.unsign(signed), "xyz")
|