summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2021-11-05 14:27:53 -0700
committerDavid Lord <davidism@gmail.com>2021-11-05 14:27:53 -0700
commit506ca21d8cc1d16313270c37b6990c9e7762f68e (patch)
tree93cb329c41bdb384c2278a3904c261b03359f62f
parent7443b9119b9ea29865c41f61e7f6e4e10873f301 (diff)
downloaditsdangerous-506ca21d8cc1d16313270c37b6990c9e7762f68e.tar.gz
remove deprecated code
-rw-r--r--CHANGES.rst8
-rw-r--r--docs/jws.rst51
-rw-r--r--src/itsdangerous/__init__.py3
-rw-r--r--src/itsdangerous/_json.py18
-rw-r--r--src/itsdangerous/jws.py259
-rw-r--r--tests/test_itsdangerous/test_jws.py143
6 files changed, 7 insertions, 475 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 78566b6..57da7ec 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -3,7 +3,13 @@ Version 2.1.0
Unreleased
- - Drop support for Python 3.6. :pr:`272`
+- Drop support for Python 3.6. :pr:`272`
+- Remove previously deprecated code. :pr:`273`
+
+ - JWS functionality: Use a dedicated library such as Authlib
+ instead.
+ - ``import itsdangerous.json``: Import ``json`` from the standard
+ library instead.
Version 2.0.2
diff --git a/docs/jws.rst b/docs/jws.rst
deleted file mode 100644
index 7d13441..0000000
--- a/docs/jws.rst
+++ /dev/null
@@ -1,51 +0,0 @@
-:orphan:
-
-.. module:: itsdangerous.jws
-
-JSON Web Signature (JWS)
-========================
-
-.. warning::
- .. deprecated:: 2.0
- ItsDangerous will no longer support JWS in version 2.1. Use a
- dedicated JWS/JWT library such as `authlib`_.
-
-.. _authlib: https://authlib.org/
-
-JSON Web Signatures (JWS) work similarly to the existing URL safe
-serializer but will emit headers according to `draft-ietf-jose-json-web
--signature <http://self-issued.info/docs/draft-ietf-jose-json-web
--signature.html>`_.
-
-.. code-block:: python
-
- from itsdangerous import JSONWebSignatureSerializer
- s = JSONWebSignatureSerializer("secret-key")
- s.dumps({"x": 42})
- 'eyJhbGciOiJIUzI1NiJ9.eyJ4Ijo0Mn0.ZdTn1YyGz9Yx5B5wNpWRL221G1WpVE5fPCPKNuc6UAo'
-
-When loading the value back the header will not be returned by default
-like with the other serializers. However it is possible to also ask for
-the header by passing ``return_header=True``. Custom header fields can
-be provided upon serialization:
-
-.. code-block:: python
-
- s.dumps(0, header_fields={"v": 1})
- 'eyJhbGciOiJIUzI1NiIsInYiOjF9.MA.wT-RZI9YU06R919VBdAfTLn82_iIQD70J_j-3F4z_aM'
- s.loads(
- "eyJhbGciOiJIUzI1NiIsInYiOjF9"
- ".MA.wT-RZI9YU06R919VBdAfTLn82_iIQD70J_j-3F4z_aM"
- )
- (0, {'alg': 'HS256', 'v': 1})
-
-ItsDangerous only provides HMAC SHA derivatives and the none algorithm
-at the moment and does not support the ECC based ones. The algorithm in
-the header is checked against the one of the serializer and on a
-mismatch a :exc:`~itsdangerous.exc.BadSignature` exception is raised.
-
-.. autoclass:: JSONWebSignatureSerializer
- :members:
-
-.. autoclass:: TimedJSONWebSignatureSerializer
- :members:
diff --git a/src/itsdangerous/__init__.py b/src/itsdangerous/__init__.py
index fc4c342..6c28c42 100644
--- a/src/itsdangerous/__init__.py
+++ b/src/itsdangerous/__init__.py
@@ -1,4 +1,3 @@
-from ._json import json
from .encoding import base64_decode as base64_decode
from .encoding import base64_encode as base64_encode
from .encoding import want_bytes as want_bytes
@@ -8,8 +7,6 @@ from .exc import BadPayload as BadPayload
from .exc import BadSignature as BadSignature
from .exc import BadTimeSignature as BadTimeSignature
from .exc import SignatureExpired as SignatureExpired
-from .jws import JSONWebSignatureSerializer
-from .jws import TimedJSONWebSignatureSerializer
from .serializer import Serializer as Serializer
from .signer import HMACAlgorithm as HMACAlgorithm
from .signer import NoneAlgorithm as NoneAlgorithm
diff --git a/src/itsdangerous/_json.py b/src/itsdangerous/_json.py
index 9368da2..c70d37a 100644
--- a/src/itsdangerous/_json.py
+++ b/src/itsdangerous/_json.py
@@ -1,6 +1,5 @@
import json as _json
import typing as _t
-from types import ModuleType
class _CompactJSON:
@@ -15,20 +14,3 @@ class _CompactJSON:
kwargs.setdefault("ensure_ascii", False)
kwargs.setdefault("separators", (",", ":"))
return _json.dumps(obj, **kwargs)
-
-
-class DeprecatedJSON(ModuleType):
- def __getattribute__(self, item: str) -> _t.Any:
- import warnings
-
- warnings.warn(
- "Importing 'itsdangerous.json' is deprecated and will be"
- " removed in ItsDangerous 2.1. Use Python's 'json' module"
- " instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return getattr(_json, item)
-
-
-json = DeprecatedJSON("json")
diff --git a/src/itsdangerous/jws.py b/src/itsdangerous/jws.py
deleted file mode 100644
index 3a6695f..0000000
--- a/src/itsdangerous/jws.py
+++ /dev/null
@@ -1,259 +0,0 @@
-import hashlib
-import time
-import warnings
-from datetime import datetime
-from datetime import timezone
-from decimal import Decimal
-from numbers import Real
-
-from ._json import _CompactJSON
-from .encoding import base64_decode
-from .encoding import base64_encode
-from .encoding import want_bytes
-from .exc import BadData
-from .exc import BadHeader
-from .exc import BadPayload
-from .exc import BadSignature
-from .exc import SignatureExpired
-from .serializer import Serializer
-from .signer import HMACAlgorithm
-from .signer import NoneAlgorithm
-
-
-class JSONWebSignatureSerializer(Serializer):
- """This serializer implements JSON Web Signature (JWS) support. Only
- supports the JWS Compact Serialization.
-
- .. deprecated:: 2.0
- Will be removed in ItsDangerous 2.1. Use a dedicated library
- such as authlib.
- """
-
- jws_algorithms = {
- "HS256": HMACAlgorithm(hashlib.sha256),
- "HS384": HMACAlgorithm(hashlib.sha384),
- "HS512": HMACAlgorithm(hashlib.sha512),
- "none": NoneAlgorithm(),
- }
-
- #: The default algorithm to use for signature generation
- default_algorithm = "HS512"
-
- default_serializer = _CompactJSON
-
- def __init__(
- self,
- secret_key,
- salt=None,
- serializer=None,
- serializer_kwargs=None,
- signer=None,
- signer_kwargs=None,
- algorithm_name=None,
- ):
- warnings.warn(
- "JWS support is deprecated and will be removed in"
- " ItsDangerous 2.1. Use a dedicated JWS/JWT library such as"
- " authlib.",
- DeprecationWarning,
- stacklevel=2,
- )
- super().__init__(
- secret_key,
- salt=salt,
- serializer=serializer,
- serializer_kwargs=serializer_kwargs,
- signer=signer,
- signer_kwargs=signer_kwargs,
- )
-
- if algorithm_name is None:
- algorithm_name = self.default_algorithm
-
- self.algorithm_name = algorithm_name
- self.algorithm = self.make_algorithm(algorithm_name)
-
- def load_payload(self, payload, serializer=None, return_header=False):
- payload = want_bytes(payload)
-
- if b"." not in payload:
- raise BadPayload('No "." found in value')
-
- base64d_header, base64d_payload = payload.split(b".", 1)
-
- try:
- json_header = base64_decode(base64d_header)
- except Exception as e:
- raise BadHeader(
- "Could not base64 decode the header because of an exception",
- original_error=e,
- ) from e
-
- try:
- json_payload = base64_decode(base64d_payload)
- except Exception as e:
- raise BadPayload(
- "Could not base64 decode the payload because of an exception",
- original_error=e,
- ) from e
-
- try:
- header = super().load_payload(json_header, serializer=_CompactJSON)
- except BadData as e:
- raise BadHeader(
- "Could not unserialize header because it was malformed",
- original_error=e,
- ) from e
-
- if not isinstance(header, dict):
- raise BadHeader("Header payload is not a JSON object", header=header)
-
- payload = super().load_payload(json_payload, serializer=serializer)
-
- if return_header:
- return payload, header
-
- return payload
-
- def dump_payload(self, header, obj):
- base64d_header = base64_encode(
- self.serializer.dumps(header, **self.serializer_kwargs)
- )
- base64d_payload = base64_encode(
- self.serializer.dumps(obj, **self.serializer_kwargs)
- )
- return base64d_header + b"." + base64d_payload
-
- def make_algorithm(self, algorithm_name):
- try:
- return self.jws_algorithms[algorithm_name]
- except KeyError:
- raise NotImplementedError("Algorithm not supported") from None
-
- def make_signer(self, salt=None, algorithm=None):
- if salt is None:
- salt = self.salt
-
- key_derivation = "none" if salt is None else None
-
- if algorithm is None:
- algorithm = self.algorithm
-
- return self.signer(
- self.secret_keys,
- salt=salt,
- sep=".",
- key_derivation=key_derivation,
- algorithm=algorithm,
- )
-
- def make_header(self, header_fields):
- header = header_fields.copy() if header_fields else {}
- header["alg"] = self.algorithm_name
- return header
-
- def dumps(self, obj, salt=None, header_fields=None):
- """Like :meth:`.Serializer.dumps` but creates a JSON Web
- Signature. It also allows for specifying additional fields to be
- included in the JWS header.
- """
- header = self.make_header(header_fields)
- signer = self.make_signer(salt, self.algorithm)
- return signer.sign(self.dump_payload(header, obj))
-
- def loads(self, s, salt=None, return_header=False):
- """Reverse of :meth:`dumps`. If requested via ``return_header``
- it will return a tuple of payload and header.
- """
- payload, header = self.load_payload(
- self.make_signer(salt, self.algorithm).unsign(want_bytes(s)),
- return_header=True,
- )
-
- if header.get("alg") != self.algorithm_name:
- raise BadHeader("Algorithm mismatch", header=header, payload=payload)
-
- if return_header:
- return payload, header
-
- return payload
-
- def loads_unsafe(self, s, salt=None, return_header=False):
- kwargs = {"return_header": return_header}
- return self._loads_unsafe_impl(s, salt, kwargs, kwargs)
-
-
-class TimedJSONWebSignatureSerializer(JSONWebSignatureSerializer):
- """Works like the regular :class:`JSONWebSignatureSerializer` but
- also records the time of the signing and can be used to expire
- signatures.
-
- JWS currently does not specify this behavior but it mentions a
- possible extension like this in the spec. Expiry date is encoded
- into the header similar to what's specified in `draft-ietf-oauth
- -json-web-token <http://self-issued.info/docs/draft-ietf-oauth-json
- -web-token.html#expDef>`_.
- """
-
- DEFAULT_EXPIRES_IN = 3600
-
- def __init__(self, secret_key, expires_in=None, **kwargs):
- super().__init__(secret_key, **kwargs)
-
- if expires_in is None:
- expires_in = self.DEFAULT_EXPIRES_IN
-
- self.expires_in = expires_in
-
- def make_header(self, header_fields):
- header = super().make_header(header_fields)
- iat = self.now()
- exp = iat + self.expires_in
- header["iat"] = iat
- header["exp"] = exp
- return header
-
- def loads(self, s, salt=None, return_header=False):
- payload, header = super().loads(s, salt, return_header=True)
-
- if "exp" not in header:
- raise BadSignature("Missing expiry date", payload=payload)
-
- int_date_error = BadHeader("Expiry date is not an IntDate", payload=payload)
-
- try:
- header["exp"] = int(header["exp"])
- except ValueError as e:
- raise int_date_error from e
-
- if header["exp"] < 0:
- raise int_date_error
-
- if header["exp"] < self.now():
- raise SignatureExpired(
- "Signature expired",
- payload=payload,
- date_signed=self.get_issue_date(header),
- )
-
- if return_header:
- return payload, header
-
- return payload
-
- def get_issue_date(self, header):
- """If the header contains the ``iat`` field, return the date the
- signature was issued, as a timezone-aware
- :class:`datetime.datetime` in UTC.
-
- .. versionchanged:: 2.0
- The timestamp is returned as a timezone-aware ``datetime``
- in UTC rather than a naive ``datetime`` assumed to be UTC.
- """
- rv = header.get("iat")
-
- if isinstance(rv, (Real, Decimal)):
- return datetime.fromtimestamp(int(rv), tz=timezone.utc)
-
- def now(self):
- return int(time.time())
diff --git a/tests/test_itsdangerous/test_jws.py b/tests/test_itsdangerous/test_jws.py
deleted file mode 100644
index 6ee8df8..0000000
--- a/tests/test_itsdangerous/test_jws.py
+++ /dev/null
@@ -1,143 +0,0 @@
-from datetime import timedelta
-
-import pytest
-
-from itsdangerous.exc import BadData
-from itsdangerous.exc import BadHeader
-from itsdangerous.exc import BadPayload
-from itsdangerous.exc import BadSignature
-from itsdangerous.exc import SignatureExpired
-from itsdangerous.jws import JSONWebSignatureSerializer
-from itsdangerous.jws import TimedJSONWebSignatureSerializer
-from test_itsdangerous.test_serializer import TestSerializer
-from test_itsdangerous.test_timed import TestTimedSerializer
-
-
-class TestJWSSerializer(TestSerializer):
- @pytest.fixture()
- def serializer_factory(self):
- def factory(secret_key="secret-key", **kwargs):
- with pytest.deprecated_call():
- return JSONWebSignatureSerializer(secret_key=secret_key, **kwargs)
-
- return factory
-
- test_signer_cls = None # type: ignore
- test_signer_kwargs = None # type: ignore
- test_fallback_signers = None # type: ignore
- test_iter_unsigners = None # type: ignore
-
- @pytest.mark.parametrize("algorithm_name", ("HS256", "HS384", "HS512", "none"))
- def test_algorithm(self, serializer_factory, algorithm_name):
- serializer = serializer_factory(algorithm_name=algorithm_name)
- assert serializer.loads(serializer.dumps("value")) == "value"
-
- def test_invalid_algorithm(self, serializer_factory):
- with pytest.raises(NotImplementedError) as exc_info:
- serializer_factory(algorithm_name="invalid")
-
- assert "not supported" in str(exc_info.value)
-
- def test_algorithm_mismatch(self, serializer_factory, serializer):
- other = serializer_factory(algorithm_name="HS256")
- other.algorithm = serializer.algorithm
- signed = other.dumps("value")
-
- with pytest.raises(BadHeader) as exc_info:
- serializer.loads(signed)
-
- assert "mismatch" in str(exc_info.value)
-
- @pytest.mark.parametrize(
- ("value", "exc_cls", "match"),
- (
- ("ab", BadPayload, '"."'),
- ("a.b", BadHeader, "base64 decode"),
- ("ew.b", BadPayload, "base64 decode"),
- ("ew.ab", BadData, "malformed"),
- ("W10.ab", BadHeader, "JSON object"),
- ),
- )
- def test_load_payload_exceptions(self, serializer, value, exc_cls, match):
- signer = serializer.make_signer()
- signed = signer.sign(value)
-
- with pytest.raises(exc_cls) as exc_info:
- serializer.loads(signed)
-
- assert match in str(exc_info.value)
-
- def test_secret_keys(self):
- with pytest.deprecated_call():
- serializer = JSONWebSignatureSerializer("a")
-
- dumped = serializer.dumps("value")
-
- with pytest.deprecated_call():
- serializer = JSONWebSignatureSerializer(["a", "b"])
-
- assert serializer.loads(dumped) == "value"
-
-
-class TestTimedJWSSerializer(TestJWSSerializer, TestTimedSerializer):
- @pytest.fixture()
- def serializer_factory(self):
- def factory(secret_key="secret-key", expires_in=10, **kwargs):
- with pytest.deprecated_call():
- return TimedJSONWebSignatureSerializer(
- secret_key=secret_key, expires_in=expires_in, **kwargs
- )
-
- return factory
-
- def test_default_expires_in(self, serializer_factory):
- serializer = serializer_factory(expires_in=None)
- assert serializer.expires_in == serializer.DEFAULT_EXPIRES_IN
-
- test_max_age = None
-
- def test_exp(self, serializer, value, ts, freeze):
- signed = serializer.dumps(value)
- freeze.tick()
- assert serializer.loads(signed) == value
- freeze.tick(timedelta(seconds=10))
-
- with pytest.raises(SignatureExpired) as exc_info:
- serializer.loads(signed)
-
- assert exc_info.value.date_signed == ts
- assert exc_info.value.payload == value
-
- test_return_payload = None
-
- def test_return_header(self, serializer, value, ts):
- signed = serializer.dumps(value)
- payload, header = serializer.loads(signed, return_header=True)
- date_signed = serializer.get_issue_date(header)
- assert (payload, date_signed) == (value, ts)
-
- def test_missing_exp(self, serializer):
- header = serializer.make_header(None)
- del header["exp"]
- signer = serializer.make_signer()
- signed = signer.sign(serializer.dump_payload(header, "value"))
-
- with pytest.raises(BadSignature):
- serializer.loads(signed)
-
- @pytest.mark.parametrize("exp", ("invalid", -1))
- def test_invalid_exp(self, serializer, exp):
- header = serializer.make_header(None)
- header["exp"] = exp
- signer = serializer.make_signer()
- signed = signer.sign(serializer.dump_payload(header, "value"))
-
- with pytest.raises(BadHeader) as exc_info:
- serializer.loads(signed)
-
- assert "IntDate" in str(exc_info.value)
-
- def test_invalid_iat(self, serializer):
- header = serializer.make_header(None)
- header["iat"] = "invalid"
- assert serializer.get_issue_date(header) is None