a BgtH @sdZddlZddlZddlZddlmZmZmZmZm Z m Z m Z m Z m Z ddlmZddlmZmZmZmZeeZd/eeee eegefe eegefeddd ZGd d d ZGd d d eZGdddejZe dddZGdddej ej!edZ"e#edddZ$d0ee e%ee#dddZ&e#edddZ'd1ee e%ee#ddd Z(ej)ed!d"d#Z*eej)d$d%d&Z+ej)ed'd(d)Z,eej)d$d*d+Z-e d,d-dZ.Gd.d-d-e"Z/dS)2zJSON (de)serialization framework. The framework presented here is somewhat based on `Go's "json" package`_ (especially the ``omitempty`` functionality). .. _`Go's "json" package`: http://golang.org/pkg/encoding/json/ N) AnyCallableDictIterableListMappingOptionalTypeTypeVar)crypto)b64errors interfacesutilF json_namedefault omitemptydecoderencoderreturncCst|||||dS)a4Convenient function to declare a :class:`Field` with proper type annotations. This function allows to write the following code: import josepy class JSON(josepy.JSONObjectWithFields): typ: str = josepy.field('type') def other_type(self) -> str: return self.typ rrrrr) _TypedFieldrr4/usr/lib/python3.9/site-packages/josepy/json_util.pyfields rc @seZdZdZdZdeeeee egefee egefddddZ e eedd d Z eedd d Z edd ddZe egefddddZe egefddddZeedddZeedddZe eedddZe eedddZdS)FieldaJSON object field. :class:`Field` is meant to be used together with :class:`JSONObjectWithFields`. ``encoder`` (``decoder``) is a callable that accepts a single parameter, i.e. a value to be encoded (decoded), and returns the serialized (deserialized) value. In case of errors it should raise :class:`~josepy.errors.SerializationError` (:class:`~josepy.errors.DeserializationError`). Note, that ``decoder`` should perform partial serialization only. :ivar str json_name: Name of the field when encoded to JSON. :ivar default: Default value (used when not present in JSON object). :ivar bool omitempty: If ``True`` and the field value is empty, then it will not be included in the serialized JSON object, and ``default`` will be used for deserialization. Otherwise, if ``False``, field is considered as required, value will always be included in the serialized JSON objected, and it must also be present when deserializing. )rrrfdecfencNFrcCs>||_||_||_|dur |jn||_|dur4|jn||_dSN)rrrdefault_decoderrdefault_encoderr)selfrrrrrrrr__init__Rs zField.__init__valuercCst|t o| S)zIs the provided value considered "empty" for this field? This is useful for subclasses that might want to override the definition of being empty, e.g. for some more exotic data types. ) isinstanceboolclsr%rrr_emptyasz Field._emptycCs||o|jS)zOmit the value in output?)r*rr"r%rrromitksz Field.omitkwargsrcKs0|j|j|j|j|jd|}t|fi|S)Nr)rrrrrtype)r"r.currentrrr_update_paramsoszField._update_params)rrcCs |j|dS)z6Descriptor to change the decoder on JSON object field.)rr1)r"rrrrrzsz Field.decoder)rrcCs |j|dS)z6Descriptor to change the encoder on JSON object field.)rr2)r"rrrrr~sz Field.encodercCs ||S)z4Decode a value, optionally with context JSON object.)rr+rrrdecodesz Field.decodecCs ||S)z4Encode a value, optionally with context JSON object.)rr+rrrencodesz Field.encodecsNt|tr tfdd|DSt|trFtfdd|DS|SdS)zDefault decoder. Recursively deserialize into immutable types ( :class:`josepy.util.frozendict` instead of :func:`dict`, :func:`tuple` instead of :func:`list`). c3s|]}|VqdSrr ).0Zsubvaluer)rr z(Field.default_decoder..cs"i|]\}}||qSrr5)r6keyr%r7rr sz)Field.default_decoder..N)r&listtupledictrZ frozendictitemsr(rr7rr s   zField.default_decodercCs|S)zDefault (passthrough) encoder.rr(rrrr!szField.default_encoder)NFNN)__name__ __module__ __qualname____doc__ __slots__strrr'rrr# classmethodr*r,r1rrr3r4r r!rrrrr7s4   rc@seZdZdZdS)raSpecialized class to mark a JSON object field with typed annotations. This class is kept private because fields are supposed to be declared using the :function:`field` in this situation. In the future the :class:`Field` may be removed in favor of this one.N)r@rArBrCrrrrrsrc@sReZdZUdZiZeeefed<e eed<ee eeee fddddZ dS)JSONObjectWithFieldsMetaaMetaclass for :class:`JSONObjectWithFields` and its subclasses. It makes sure that, for any class ``cls`` with ``__metaclass__`` set to ``JSONObjectWithFieldsMeta``: 1. All fields (attributes of type :class:`Field`) in the class definition are moved to the ``cls._fields`` dictionary, where keys are field attribute names and values are fields themselves. 2. ``cls.__slots__`` is extended by all field attribute names (i.e. not :attr:`Field.json_name`). Original ``cls.__slots__`` are stored in ``cls._orig_slots``. In a consequence, for a field attribute name ``some_field``, ``cls.some_field`` will be a slot descriptor and not an instance of :class:`Field`. For example:: some_field = Field('someField', default=()) class Foo: __metaclass__ = JSONObjectWithFieldsMeta __slots__ = ('baz',) some_field = some_field assert Foo.__slots__ == ('some_field', 'baz') assert Foo._orig_slots == () assert Foo.some_field is not Field assert Foo._fields.keys() == ['some_field'] assert Foo._fields['some_field'] is some_field As an implementation note, this metaclass inherits from :class:`abc.ABCMeta` (and not the usual :class:`type`) to mitigate the metaclass conflict (:class:`ImmutableMap` and :class:`JSONDeSerializable`, parents of :class:`JSONObjectWithFields`, use :class:`abc.ABCMeta` as its metaclass). _fields _orig_slots)namebases namespacercCsi}|D]}|t|diq|D]P\}}t|tr,t|trn||divrntd|d|d| |||<q,|dd|d<t t |dt | |d<||d<t j||||S) NrH__annotations__zField `z` in JSONObject `z` has no type annotation.rDrrI)updategetattrcopyr?r&rrget ValueErrorpopr=r<keysabcABCMeta__new__)ZmcsrJrKrLfieldsbaser:r%rrrrWs   z JSONObjectWithFieldsMeta.__new__N) r@rArBrCrHrrErrMrrrrWrrrrrGs ' rGGenericJSONObjectWithFieldsJSONObjectWithFields)boundcseZdZdZeeeefdddZeddfdd Z eed d d Z eeefdd d Z eeefdddZ ee eefddddZee eefedddZeeee eefedddZZS)r[aJSON object with fields. Example:: class Foo(JSONObjectWithFields): bar = Field('Bar') empty = Field('Empty', omitempty=True) @bar.encoder def bar(value): return value + 'bar' @bar.decoder def bar(value): if not value.endswith('bar'): raise errors.DeserializationError('No bar suffix!') return value[:-3] assert Foo(bar='baz').to_partial_json() == {'Bar': 'bazbar'} assert Foo.from_json({'Bar': 'bazbar'}) == Foo(bar='baz') assert (Foo.from_json({'Bar': 'bazbar', 'Empty': '!'}) == Foo(bar='baz', empty='!')) assert Foo(bar='baz').bar == 'baz' rcCsdd|jDS)zGet default fields values.cSsi|]\}}||jqSr)r)r6slotrrrrr;r9z2JSONObjectWithFields._defaults..)rHr?r7rrr _defaultsszJSONObjectWithFields._defaultsNr-c s"tjfii||dSr)superr#r_)r"r. __class__rrr#szJSONObjectWithFields.__init__)rJrcCsBz|j|}Wn"ty0td|Yn0|t||S)zEncode a single field. :param str name: Name of the field to be encoded. :raises errors.SerializationError: if field cannot be serialized :raises errors.Error: if field could not be found zField not found: {0})rHKeyErrorr Errorformatr4rO)r"rJrrrrr4s  zJSONObjectWithFields.encodec Csi}t}|jD]|\}}t||}||r@|||fqz||||j<Wqtj y}z t d |||WYd}~qd}~00q|S)zSerialize fields to JSON.zCould not encode {0} ({1}): {2}N) setrHr?rOr,addr4rr ZSerializationErrorre)r"jobjZomittedr^rr%errorrrrfields_to_partial_json/s   z+JSONObjectWithFields.fields_to_partial_jsoncCs|Sr)rj)r"rrrto_partial_jsonAsz$JSONObjectWithFields.to_partial_jsonrhrcCsTt}|jD]$\}}|js|j|vr||jq|rPtdd |dS)Nz&The following fields are required: {0},) rfrHr?rrrgr DeserializationErrorrejoin)r)rhmissing_rrrr_check_requiredDsz$JSONObjectWithFields._check_requiredc Cs||i}|jD]|\}}|j|vr<|jr<|j||<q||j}z||||<Wqtjy}z td |||WYd}~qd}~00q|S)zDeserialize fields from JSON.z#Could not decode {0!r} ({1!r}): {2}N) rrrHr?rrrr3r rnre)r)rhrXr^rr%rirrrfields_from_jsonPs    z%JSONObjectWithFields.fields_from_json)r)rhrcCs|fi||Sr)rs)r)rhrrr from_jsonbszJSONObjectWithFields.from_json)r@rArBrCrFrrErr_r#r4rjrkrrrrsr rZrt __classcell__rrrarr[s ) metaclass)datarcCst|dS)zJEncode JOSE Base-64 field. :param bytes data: :rtype: `str` ascii)r Z b64encoder3)rwrrrencode_b64joseisry)rwsizeminimumrc Cszt|}Wn0tjyB}zt|WYd}~n d}~00|dur||s\t||ksl|r|t||kr|td||S)aDecode JOSE Base-64 field. :param unicode data: :param int size: Required length (after decoding). :param bool minimum: If ``True``, then `size` will be treated as minimum required length, as opposed to exact equality. :rtype: bytes Nz&Expected at least or exactly {0} bytes) r Z b64decoder4binasciirdr rnlenre)rwrzr{Zdecodedrirrrdecode_b64josets    r~r$cCst|S)z;Hexlify. :param bytes value: :rtype: unicode )r|Zhexlifyr3)r%rrr encode_hex16sr)r%rzr{rc Cs|}|dur@|s$t||dks8|r@t||dkr@tz t|WStjy|}zt|WYd}~n d}~00dS)aDecode hexlified field. :param unicode value: :param int size: Required length (after decoding). :param bool minimum: If ``True``, then `size` will be treated as minimum required length, as opposed to exact equality. :rtype: bytes N)r4r}r rnr|Z unhexlifyrd)r%rzr{Zvalue_brirrr decode_hex16s  r)certrcCs*t|jtjrtdtttj|jS)zEncode certificate as JOSE Base-64 DER. :type cert: `OpenSSL.crypto.X509` wrapped in `.ComparableX509` :rtype: unicode z.Error input is actually a certificate request.)r&wrappedr ZX509ReqrRryZdump_certificate FILETYPE_ASN1)rrrr encode_certsr)b64derrc CsPztttjt|WStjyJ}zt|WYd}~n d}~00dS)zDecode JOSE Base-64 DER-encoded certificate. :param unicode b64der: :rtype: `OpenSSL.crypto.X509` wrapped in `.ComparableX509` N) rComparableX509r Zload_certificaterr~rdr rnrrirrr decode_certs r)csrrcCs*t|jtjrtdtttj|jS)zEncode CSR as JOSE Base-64 DER. :type csr: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509` :rtype: unicode z&Error input is actually a certificate.)r&rr ZX509rRryZdump_certificate_requestr)rrrr encode_csrsrc CsPztttjt|WStjyJ}zt|WYd}~n d}~00dS)zDecode JOSE Base-64 DER-encoded CSR. :param unicode b64der: :rtype: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509` N) rrr Zload_certificate_requestrr~rdr rnrrrr decode_csrs r GenericTypedJSONObjectWithFieldsTypedJSONObjectWithFieldsc@seZdZUdZeZeed<dZeed<eZ e ee fed<e de e eee e ddd Ze eeefe dd d d Ze eefd ddZe eeefdd ddZdS)rzJSON object with type.typr/type_field_nameTYPESN)type_clsrrcCs |dur|jn|}||j|<|S)z(Register class for JSON deserialization.N)rr)r)rrrrrregisters z"TypedJSONObjectWithFields.registerrlcCs||jvr.|j|vr*td|j|St|tsHtd|z||j}WntyrtdYn0z |j|WStyt ||Yn0dS)z&Get the registered class for ``jobj``.zMissing type field ({0})z{0} is not a dictionary objectzmissing type fieldN) rvaluesrr rnrer&r>rcZUnrecognizedTypeError)r)rhrrrr get_type_clss       z&TypedJSONObjectWithFields.get_type_clsr]cCs|}|j||j<|S)aGet JSON serializable object. :returns: Serializable JSON object representing ACME typed object. :meth:`validate` will almost certainly not work, due to reasons explained in :class:`josepy.interfaces.IJSONSerializable`. :rtype: dict )rjrr)r"rhrrrrks  z)TypedJSONObjectWithFields.to_partial_jsoncCs||}|fi||S)zDeserialize ACME object from valid JSON object. :raises josepy.errors.UnrecognizedTypeError: if type of the ACME object has not been registered. )rrs)r)rhrrrrrt(s z#TypedJSONObjectWithFields.from_json)N)r@rArBrCNotImplementedrrErMrrrr rFrrrrrrrkrtrrrrrs     )NFNN)NF)NF)0rCrUr|Zloggingtypingrrrrrrrr r ZOpenSSLr Zjosepyr r rrZ getLoggerr@loggerrEr'rrrrVrGrZZ ImmutableMapZJSONDeSerializabler[bytesryintr~rrrrrrrrrrrrrsL,   q F   o