| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 | import jsonimport typing as _tfrom .encoding import want_bytesfrom .exc import BadPayloadfrom .exc import BadSignaturefrom .signer import _make_keys_listfrom .signer import Signer_t_str_bytes = _t.Union[str, bytes]_t_opt_str_bytes = _t.Optional[_t_str_bytes]_t_kwargs = _t.Dict[str, _t.Any]_t_opt_kwargs = _t.Optional[_t_kwargs]_t_signer = _t.Type[Signer]_t_fallbacks = _t.List[_t.Union[_t_kwargs, _t.Tuple[_t_signer, _t_kwargs], _t_signer]]_t_load_unsafe = _t.Tuple[bool, _t.Any]_t_secret_key = _t.Union[_t.Iterable[_t_str_bytes], _t_str_bytes]def is_text_serializer(serializer: _t.Any) -> bool:    """Checks whether a serializer generates text or binary."""    return isinstance(serializer.dumps({}), str)class Serializer:    """A serializer wraps a :class:`~itsdangerous.signer.Signer` to    enable serializing and securely signing data other than bytes. It    can unsign to verify that the data hasn't been changed.    The serializer provides :meth:`dumps` and :meth:`loads`, similar to    :mod:`json`, and by default uses :mod:`json` internally to serialize    the data to bytes.    The secret key should be a random string of ``bytes`` and should not    be saved to code or version control. Different salts should be used    to distinguish signing in different contexts. See :doc:`/concepts`    for information about the security of the secret key and salt.    :param secret_key: The secret key to sign and verify with. Can be a        list of keys, oldest to newest, to support key rotation.    :param salt: Extra key to combine with ``secret_key`` to distinguish        signatures in different contexts.    :param serializer: An object that provides ``dumps`` and ``loads``        methods for serializing data to a string. Defaults to        :attr:`default_serializer`, which defaults to :mod:`json`.    :param serializer_kwargs: Keyword arguments to pass when calling        ``serializer.dumps``.    :param signer: A ``Signer`` class to instantiate when signing data.        Defaults to :attr:`default_signer`, which defaults to        :class:`~itsdangerous.signer.Signer`.    :param signer_kwargs: Keyword arguments to pass when instantiating        the ``Signer`` class.    :param fallback_signers: List of signer parameters to try when        unsigning with the default signer fails. Each item can be a dict        of ``signer_kwargs``, a ``Signer`` class, or a tuple of        ``(signer, signer_kwargs)``. Defaults to        :attr:`default_fallback_signers`.    .. versionchanged:: 2.0        Added support for key rotation by passing a list to        ``secret_key``.    .. versionchanged:: 2.0        Removed the default SHA-512 fallback signer from        ``default_fallback_signers``.    .. versionchanged:: 1.1        Added support for ``fallback_signers`` and configured a default        SHA-512 fallback. This fallback is for users who used the yanked        1.0.0 release which defaulted to SHA-512.    .. versionchanged:: 0.14        The ``signer`` and ``signer_kwargs`` parameters were added to        the constructor.    """    #: The default serialization module to use to serialize data to a    #: string internally. The default is :mod:`json`, but can be changed    #: to any object that provides ``dumps`` and ``loads`` methods.    default_serializer: _t.Any = json    #: The default ``Signer`` class to instantiate when signing data.    #: The default is :class:`itsdangerous.signer.Signer`.    default_signer: _t_signer = Signer    #: The default fallback signers to try when unsigning fails.    default_fallback_signers: _t_fallbacks = []    def __init__(        self,        secret_key: _t_secret_key,        salt: _t_opt_str_bytes = b"itsdangerous",        serializer: _t.Any = None,        serializer_kwargs: _t_opt_kwargs = None,        signer: _t.Optional[_t_signer] = None,        signer_kwargs: _t_opt_kwargs = None,        fallback_signers: _t.Optional[_t_fallbacks] = None,    ):        #: The list of secret keys to try for verifying signatures, from        #: oldest to newest. The newest (last) key is used for signing.        #:        #: This allows a key rotation system to keep a list of allowed        #: keys and remove expired ones.        self.secret_keys: _t.List[bytes] = _make_keys_list(secret_key)        if salt is not None:            salt = want_bytes(salt)            # if salt is None then the signer's default is used        self.salt = salt        if serializer is None:            serializer = self.default_serializer        self.serializer: _t.Any = serializer        self.is_text_serializer: bool = is_text_serializer(serializer)        if signer is None:            signer = self.default_signer        self.signer: _t_signer = signer        self.signer_kwargs: _t_kwargs = signer_kwargs or {}        if fallback_signers is None:            fallback_signers = list(self.default_fallback_signers or ())        self.fallback_signers: _t_fallbacks = fallback_signers        self.serializer_kwargs: _t_kwargs = serializer_kwargs or {}    @property    def secret_key(self) -> bytes:        """The newest (last) entry in the :attr:`secret_keys` list. This        is for compatibility from before key rotation support was added.        """        return self.secret_keys[-1]    def load_payload(        self, payload: bytes, serializer: _t.Optional[_t.Any] = None    ) -> _t.Any:        """Loads the encoded object. This function raises        :class:`.BadPayload` if the payload is not valid. The        ``serializer`` parameter can be used to override the serializer        stored on the class. The encoded ``payload`` should always be        bytes.        """        if serializer is None:            serializer = self.serializer            is_text = self.is_text_serializer        else:            is_text = is_text_serializer(serializer)        try:            if is_text:                return serializer.loads(payload.decode("utf-8"))            return serializer.loads(payload)        except Exception as e:            raise BadPayload(                "Could not load the payload because an exception"                " occurred on unserializing the data.",                original_error=e,            ) from e    def dump_payload(self, obj: _t.Any) -> bytes:        """Dumps the encoded object. The return value is always bytes.        If the internal serializer returns text, the value will be        encoded as UTF-8.        """        return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))    def make_signer(self, salt: _t_opt_str_bytes = None) -> Signer:        """Creates a new instance of the signer to be used. The default        implementation uses the :class:`.Signer` base class.        """        if salt is None:            salt = self.salt        return self.signer(self.secret_keys, salt=salt, **self.signer_kwargs)    def iter_unsigners(self, salt: _t_opt_str_bytes = None) -> _t.Iterator[Signer]:        """Iterates over all signers to be tried for unsigning. Starts        with the configured signer, then constructs each signer        specified in ``fallback_signers``.        """        if salt is None:            salt = self.salt        yield self.make_signer(salt)        for fallback in self.fallback_signers:            if isinstance(fallback, dict):                kwargs = fallback                fallback = self.signer            elif isinstance(fallback, tuple):                fallback, kwargs = fallback            else:                kwargs = self.signer_kwargs            for secret_key in self.secret_keys:                yield fallback(secret_key, salt=salt, **kwargs)    def dumps(self, obj: _t.Any, salt: _t_opt_str_bytes = None) -> _t_str_bytes:        """Returns a signed string serialized with the internal        serializer. The return value can be either a byte or unicode        string depending on the format of the internal serializer.        """        payload = want_bytes(self.dump_payload(obj))        rv = self.make_signer(salt).sign(payload)        if self.is_text_serializer:            return rv.decode("utf-8")        return rv    def dump(self, obj: _t.Any, f: _t.IO, salt: _t_opt_str_bytes = None) -> None:        """Like :meth:`dumps` but dumps into a file. The file handle has        to be compatible with what the internal serializer expects.        """        f.write(self.dumps(obj, salt))    def loads(        self, s: _t_str_bytes, salt: _t_opt_str_bytes = None, **kwargs: _t.Any    ) -> _t.Any:        """Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the        signature validation fails.        """        s = want_bytes(s)        last_exception = None        for signer in self.iter_unsigners(salt):            try:                return self.load_payload(signer.unsign(s))            except BadSignature as err:                last_exception = err        raise _t.cast(BadSignature, last_exception)    def load(self, f: _t.IO, salt: _t_opt_str_bytes = None) -> _t.Any:        """Like :meth:`loads` but loads from a file."""        return self.loads(f.read(), salt)    def loads_unsafe(        self, s: _t_str_bytes, salt: _t_opt_str_bytes = None    ) -> _t_load_unsafe:        """Like :meth:`loads` but without verifying the signature. This        is potentially very dangerous to use depending on how your        serializer works. The return value is ``(signature_valid,        payload)`` instead of just the payload. The first item will be a        boolean that indicates if the signature is valid. This function        never fails.        Use it for debugging only and if you know that your serializer        module is not exploitable (for example, do not use it with a        pickle serializer).        .. versionadded:: 0.15        """        return self._loads_unsafe_impl(s, salt)    def _loads_unsafe_impl(        self,        s: _t_str_bytes,        salt: _t_opt_str_bytes,        load_kwargs: _t_opt_kwargs = None,        load_payload_kwargs: _t_opt_kwargs = None,    ) -> _t_load_unsafe:        """Low level helper function to implement :meth:`loads_unsafe`        in serializer subclasses.        """        if load_kwargs is None:            load_kwargs = {}        try:            return True, self.loads(s, salt=salt, **load_kwargs)        except BadSignature as e:            if e.payload is None:                return False, None            if load_payload_kwargs is None:                load_payload_kwargs = {}            try:                return (                    False,                    self.load_payload(e.payload, **load_payload_kwargs),                )            except BadPayload:                return False, None    def load_unsafe(self, f: _t.IO, salt: _t_opt_str_bytes = None) -> _t_load_unsafe:        """Like :meth:`loads_unsafe` but loads from a file.        .. versionadded:: 0.15        """        return self.loads_unsafe(f.read(), salt=salt)
 |