itsdangerous-1.1.0/0000755000175000017500000000000013364726771014440 5ustar daviddavid00000000000000itsdangerous-1.1.0/CHANGES.rst0000644000175000017500000001257713364722776016257 0ustar daviddavid00000000000000Version 1.1.0 ------------- Released 2018-10-26 - Change default signing algorithm back to SHA-1. (`#113`_) - Added a default SHA-512 fallback for users who used the yanked 1.0.0 release which defaulted to SHA-512. (`#114`_) - Add support for fallback algorithms during deserialization to support changing the default in the future without breaking existing signatures. (`#113`_) - Changed capitalization of packages back to lowercase as the change in capitalization broke some tooling. (`#113`_) .. _#113: https://github.com/pallets/itsdangerous/pull/113 .. _#114: https://github.com/pallets/itsdangerous/pull/114 Version 1.0.0 ------------- Released 2018-10-18 YANKED *Note*: This release was yanked from PyPI because it changed the default algorithm to SHA-512. This decision was reverted in 1.1.0 and it remains at SHA1. - Drop support for Python 2.6 and 3.3. - Refactor code from a single module to a package. Any object in the API docs is still importable from the top-level ``itsdangerous`` name, but other imports will need to be changed. A future release will remove many of these compatibility imports. (`#107`_) - Optimize how timestamps are serialized and deserialized. (`#13`_) - ``base64_decode`` raises ``BadData`` when it is passed invalid data. (`#27`_) - Ensure value is bytes when signing to avoid a ``TypeError`` on Python 3. (`#29`_) - Add a ``serializer_kwargs`` argument to ``Serializer``, which is passed to ``dumps`` during ``dump_payload``. (`#36`_) - More compact JSON dumps for unicode strings. (`#38`_) - Use the full timestamp rather than an offset, allowing dates before 2011. (`#46`_) - Detect a ``sep`` character that may show up in the signature itself and raise a ``ValueError``. (`#62`_) - Use a consistent signature for keyword arguments for ``Serializer.load_payload`` in subclasses. (`#74`_, `#75`_) - Change default intermediate hash from SHA-1 to SHA-512. (`#80`_) - Convert JWS exp header to an int when loading. (`#99`_) .. _#13: https://github.com/pallets/itsdangerous/pull/13 .. _#27: https://github.com/pallets/itsdangerous/pull/27 .. _#29: https://github.com/pallets/itsdangerous/issues/29 .. _#36: https://github.com/pallets/itsdangerous/pull/36 .. _#38: https://github.com/pallets/itsdangerous/issues/38 .. _#46: https://github.com/pallets/itsdangerous/issues/46 .. _#62: https://github.com/pallets/itsdangerous/issues/62 .. _#74: https://github.com/pallets/itsdangerous/issues/74 .. _#75: https://github.com/pallets/itsdangerous/pull/75 .. _#80: https://github.com/pallets/itsdangerous/pull/80 .. _#99: https://github.com/pallets/itsdangerous/pull/99 .. _#107: https://github.com/pallets/itsdangerous/pull/107 Version 0.24 ------------ Released 2014-03-28 - Added a ``BadHeader`` exception that is used for bad headers that replaces the old ``BadPayload`` exception that was reused in those cases. Version 0.23 ------------ Released 2013-08-08 - Fixed a packaging mistake that caused the tests and license files to not be included. Version 0.22 ------------ Released 2013-07-03 - Added support for ``TimedJSONWebSignatureSerializer``. - Made it possible to override the signature verification function to allow implementing asymmetrical algorithms. Version 0.21 ------------ Released 2013-05-26 - Fixed an issue on Python 3 which caused invalid errors to be generated. Version 0.20 ------------ Released 2013-05-23 - Fixed an incorrect call into ``want_bytes`` that broke some uses of itsdangerous on Python 2.6. Version 0.19 ------------ Released 2013-05-21 - Dropped support for 2.5 and added support for 3.3. Version 0.18 ------------ Released 2013-05-03 - Added support for JSON Web Signatures (JWS). Version 0.17 ------------ Released 2012-08-10 - Fixed a name error when overriding the digest method. Version 0.16 ------------ Released 2012-07-11 - Made it possible to pass unicode values to ``load_payload`` to make it easier to debug certain things. Version 0.15 ------------ Released 2012-07-11 - Made standalone ``load_payload`` more robust by raising one specific error if something goes wrong. - Refactored exceptions to catch more cases individually, added more attributes. - Fixed an issue that caused ``load_payload`` not work in some situations with timestamp based serializers - Added an ``loads_unsafe`` method. Version 0.14 ------------ Released 2012-06-29 - API refactoring to support different key derivations. - Added attributes to exceptions so that you can inspect the data even if the signature check failed. Version 0.13 ------------ Released 2012-06-10 - Small API change that enables customization of the digest module. Version 0.12 ------------ Released 2012-02-22 - Fixed a problem with the local timezone being used for the epoch calculation. This might invalidate some of your signatures if you were not running in UTC timezone. You can revert to the old behavior by monkey patching ``itsdangerous.EPOCH``. Version 0.11 ------------ Released 2011-07-07 - Fixed an uncaught value error. Version 0.10 ------------ Released 2011-06-25 - Refactored interface that the underlying serializers can be swapped by passing in a module instead of having to override the payload loaders and dumpers. This makes the interface more compatible with Django's recent changes. itsdangerous-1.1.0/LICENSE.rst0000644000175000017500000000411013364715721016241 0ustar daviddavid00000000000000`BSD 3-Clause `_ Copyright © 2011 by the Pallets team. Some rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. We kindly ask you to use these themes in an unmodified manner only with Pallets and Pallets-related projects, not for unrelated projects. If you like the visual style and want to use it for your own projects, please consider making some larger changes to the themes (such as changing font faces, sizes, colors or margins). THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---- The initial implementation of itsdangerous was inspired by Django's signing module. Copyright © Django Software Foundation and individual contributors. All rights reserved. itsdangerous-1.1.0/MANIFEST.in0000644000175000017500000000024413364710266016166 0ustar daviddavid00000000000000include CHANGES.rst include LICENSE.rst include README.rst include tox.ini graft docs prune docs/_build graft tests prune tests/.hypothesis global-exclude *.py[co] itsdangerous-1.1.0/PKG-INFO0000644000175000017500000000704113364726771015537 0ustar daviddavid00000000000000Metadata-Version: 1.2 Name: itsdangerous Version: 1.1.0 Summary: Various helpers to pass data to untrusted environments and back. Home-page: https://palletsprojects.com/p/itsdangerous/ Author: Armin Ronacher Author-email: armin.ronacher@active-4.com Maintainer: Pallets Team Maintainer-email: contact@palletsprojects.com License: BSD Project-URL: Documentation, https://itsdangerous.palletsprojects.com/ Project-URL: Code, https://github.com/pallets/itsdangerous Project-URL: Issue tracker, https://github.com/pallets/itsdangerous/issues Description: itsdangerous ============ ... so better sign this Various helpers to pass data to untrusted environments and to get it back safe and sound. Data is cryptographically signed to ensure that a token has not been tampered with. It's possible to customize how data is serialized. Data is compressed as needed. A timestamp can be added and verified automatically while loading a token. Installing ---------- Install and update using `pip`_: .. code-block:: text pip install -U itsdangerous .. _pip: https://pip.pypa.io/en/stable/quickstart/ A Simple Example ---------------- Here's how you could generate a token for transmitting a user's id and name between web requests. .. code-block:: python from itsdangerous import URLSafeSerializer auth_s = URLSafeSerializer("secret key", "auth") token = auth_s.dumps({"id": 5, "name": "itsdangerous"}) print(token) # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg data = auth_s.loads(token) print(data["name"]) # itsdangerous Donate ------ The Pallets organization develops and supports itsdangerous and other popular packages. In order to grow the community of contributors and users, and allow the maintainers to devote more time to the projects, `please donate today`_. .. _please donate today: https://palletsprojects.com/donate Links ----- * Website: https://palletsprojects.com/p/itsdangerous/ * Documentation: https://itsdangerous.palletsprojects.com/ * License: `BSD `_ * Releases: https://pypi.org/project/itsdangerous/ * Code: https://github.com/pallets/itsdangerous * Issue tracker: https://github.com/pallets/itsdangerous/issues * Test status: https://travis-ci.org/pallets/itsdangerous * Test coverage: https://codecov.io/gh/pallets/itsdangerous Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* itsdangerous-1.1.0/README.rst0000644000175000017500000000350113364715721016117 0ustar daviddavid00000000000000itsdangerous ============ ... so better sign this Various helpers to pass data to untrusted environments and to get it back safe and sound. Data is cryptographically signed to ensure that a token has not been tampered with. It's possible to customize how data is serialized. Data is compressed as needed. A timestamp can be added and verified automatically while loading a token. Installing ---------- Install and update using `pip`_: .. code-block:: text pip install -U itsdangerous .. _pip: https://pip.pypa.io/en/stable/quickstart/ A Simple Example ---------------- Here's how you could generate a token for transmitting a user's id and name between web requests. .. code-block:: python from itsdangerous import URLSafeSerializer auth_s = URLSafeSerializer("secret key", "auth") token = auth_s.dumps({"id": 5, "name": "itsdangerous"}) print(token) # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg data = auth_s.loads(token) print(data["name"]) # itsdangerous Donate ------ The Pallets organization develops and supports itsdangerous and other popular packages. In order to grow the community of contributors and users, and allow the maintainers to devote more time to the projects, `please donate today`_. .. _please donate today: https://palletsprojects.com/donate Links ----- * Website: https://palletsprojects.com/p/itsdangerous/ * Documentation: https://itsdangerous.palletsprojects.com/ * License: `BSD `_ * Releases: https://pypi.org/project/itsdangerous/ * Code: https://github.com/pallets/itsdangerous * Issue tracker: https://github.com/pallets/itsdangerous/issues * Test status: https://travis-ci.org/pallets/itsdangerous * Test coverage: https://codecov.io/gh/pallets/itsdangerous itsdangerous-1.1.0/docs/0000755000175000017500000000000013364726771015370 5ustar daviddavid00000000000000itsdangerous-1.1.0/docs/Makefile0000644000175000017500000000110513364710266017015 0ustar daviddavid00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) itsdangerous-1.1.0/docs/_static/0000755000175000017500000000000013364726771017016 5ustar daviddavid00000000000000itsdangerous-1.1.0/docs/_static/itsdangerous-logo-sidebar.png0000644000175000017500000001474513364710266024603 0ustar daviddavid00000000000000PNG  IHDR&zsBIT|dIDATxyeDF`ldR6 (SZjhZbp  f#wJSۼ'p"; !<=&dL;` :b+ z|l < Fx_%HY^@+7bϿ |܎]ӀAC/n7Ptޟ{Oˀ@a`k7Ph<؋@G+9;f@)\9p2p0%pv6 ȉ'@%ÀO1h4^Ȳwr@Hʀ}hYc0iPjxqN@XUۛ ((#v8 ĮEEJ"͆HpdE[TǤqtCM]W$R xb.B_؂8$Z}ђƖƃxWEce1l#9|*+ *amv0Z(a?%S)%`G/`d8OH(>Z% b '[0k@.B]k(=pR#xľ~|KS+M2-aZ+v꒥L۩8gZPIuB3a#_nAR+.LtǑFan?#;6ż}@NUҁ*=5X8qZe##s>nA]&8"V1":cswcM 8h1)2 5U)ZEp$wǒx؞[~(ҁaub!og1:q.!7>xw>ZFLH%!^ V/8[Ļ}Ņ~@b"X?S*p^ʘHq:Hro-wDj^vHvJfQ:!7#V#񦢂m?INox48y mr2Mwx ׆J:06 DFcD{$4b&oW 4F|n6`l,"J$ޯQXl_?Qk6ٞAuɎG d }ᨭE@_lJ:czKo_} }GzA6_P*mǠo 팧*/7 7#ᅅ~A],&"%O1E2S-"o>( I~x7I׻ud|BBCOވ2`'PK_Mx-ޟ0xqu_@pE@џ[{\qvJGrcc 뀢G*QEC7Xxs(Ϻuc{oA,F4j I5S&6G=Bid2[" ~c@ Q>ݶ؊x`}Z [r1)0L2w0| Fĩa{$0Q !^oL;p"vC^"!^R* #bb8C[h~o CT18|?6^[nC+yc WJdX \/k{)7cBvd,^@F€ xp$ٍxkjn{҇2ڮӥrx=(zb# E@Fk=ěyP~ g6F02nxy$Ļy?U((Q mGaR@18Kb;稼GFK4D2}(a:3'bOظܚ"~TCLd}POwO?8`%Q>݁HQlJ%8~ z|hJb: C? Ƕh=b"!t@@5ī~>T+pr-\|LHUNr@)=1R= sV4nDwLm-[+ {bA$p8ᱻKi(>@eQ!aFUs]x#nul^KF+]M܊)Я|ah' /H13sU!8( >=WF&4K{ݒnjhW B??Jܻc1rxkFv@a(ꈖ쳽[_(@脳NƠO'Xe qRU-^m-Ges ^@Ftc7aZsk1g0½eQ AhI^ ^@Ntȳ +0z#*'! '7)P%PG @尖XGUB"ގEKa$[xc ȉޘ2i>]w +`6G'c@yvO kH2AH +1$_ ꔀ{v²Xw<^F]m;t|ʱ֫{w I žuE!qKHފo+6lT,lěq Q/r^ {~msu: >Fc:,MAk/fXg^K܇O3.GP$ī.Y'b}Rpӱl2ՉF?z.Gߠ^&c$IJl"}k@hiO~DBO+CPD8cx5ZheR$; &ѣՏː`-u[_}pVˑ3J' p.fcp 1ka0 O~/ر?6oUapw d$Lލ[n>,>Fm9#IeH8W+aI'$CG d$8L/b'8"E2q&DkCc̿@uC[ۘpkPWp:GbJRy[#! hdprA1Fg"1vXtqwǣu/ @),,h+$Bb42b$=|/z﷘l.~wz0hZpV8p 1xxr/BFO` &7nr HDR'#Ibfim90x@Q&B. 2`"r֘*YJBǣ70"% 02?Bh$+aOEkFͮ h\[6ĄۂA>DYQҸb̓ bũXĄۊ =MIryt &~Ֆa͹P|h]j.DM E닖o FzoSvITsޏďxl%&ӻZX&cJ@@F,2T%@`+6nQW zc> ):Rny{z$XT%s@ Nu0Ut$,q '#tIPg{j[nc6Fg`$ܓ$Lxuz44X@E?7x">Lhb gk_P'oyqyHtS-Bj:Q O߀|bcsE;r8|?*iřq;P |H[k59^S@@T_h3j2XTU e$fSrD;ʲa$tƘXF";Ou݉I&͌_M-DP”n-/8pSuLm@>T-NN8Γ}0nc ,[!f({ш$wػweÕ`5'j@πo_%a,F#l9-uTM{ae`XدkTbP*Ez%gy5D.^/e= ڹ{-{`6h:jkkZ ʿ(9O 08uCl+&yYm@%F0pLRNt>.}S-ͪ/Ps<=uQG-gn Ua .LE' [ Ttb@)~tI1_wPh]do`8sw' IO]ֻ  uͬed:62AALO/kffO~ol ifY.kAD`Xk30p?;AAt&=OlN'$ٶv!fG4 N2[90*=3Y 6K@VW9lVtkoP3AAIxX }_6vkml]V?f>ѝ p;a(  X̶kwX   Jj`{`< f@.1k~l AA pGYQDc@/4pG3۬v ;a wA l_`2QU`07& w۠B} ~YC{M0%rM&GǺO\wkz[f#9CWAA*:6`G`f6.֠afY9IBб3 κ6;e/@ HlF3"3or`I ppmaY%vrF/.U|W ߞh =_}00#zxc`O܎*=`5`S3;Ef=}sߟ^A Gi s# 91>~^>XhfYMQdڛދx =p3AA}镟oR0+o‚yq`U`gZ3+wA f-zRXh'ŀ ?dQllr`g`Z`xt5f6h4f6zyË́G_{]!hNN^_gj<"haOz3ݟlSȿ Atb^8f9-Y}_a;%nkY-f6+p$Du̾B-JU I~#〟Ț+& ||G `u{kmn93ݿgirt~|zKz ۙ X]-gi  Ϲ+e 'rf.cV3YAꎶc~F`g>9 ,?p[fעlxzg>Á*f;:k|\^rlPdqMSE+'"nrc)tπ.瀵gTxl7`Ywc9;61bfrE{(UJNFo4df?"ߖa-HNnVKZ<,/ְcf EuG18T:㑸յnlÀݽh:lcLZDB/P`̬w_ m/~;|7iy#&}Ebk9Moyۢ33W/{krۜs !0=ieXw\uKsn-ah}t,l ~Dsݽ*맙Nk1mro}wK ~_<zςjJ|~`;d^)Q#QD6,NEoτ n8܅2W2anMw餿ޅ鼭:4]e4 SZ秴%L?5u>uthc"7:als,0cSv^4hzea[G9.F?ˠ_e(r̍vs HL/ lTks{2B*Ǜu>3}ÁπʹҹY7 Ŷo o_^5;K^V-e߁ݝr7t,RKZHA=#_YQ'IW3زum2כ[ۙBjo2;8Ӈ+F9s7r |v.rCqdUX~v@p2evvEPY[jT8!}l?e7 =by[$p^>O˃ N*Z Y}}X.qާz BeY^p"~O@c3#1"ꅜ7Do dJ%= X*ep Ա?Eqw8" EQl>Q+~,H~/?-梭ɞU<0A?_Pm7oj锻?!9weĭ&ZEtQ7Je73;̖@2uQմE֭OqJ)vjW{ۻ !̡WƫIڲot`g@ ߠSJ[!0+rF7dPn?&xv]˯zy+HTQPtuusu?ϣYޢZ5 (AB:o=V'z}VtSN/07"4f|}_+ߺ'Ne!ݠAp? OzCsY>s@2/795+X /Sw WzǹKr7wt5~B7lzf6gQ#i=*]6"x)rZ@cZY\ſyR UN$^sVtDCBzf̦ANVCuHlQB;aM 3ukS)npe:X6@H2-.5q=D^W27 9:hpCҿ7~{蚉x;ȴkho_9Ί\])7S!!fQn(|;jvgЭ,3A۪3ںSu_nTȉ8-v LOdxfg 'J|4UVFE Jk%O3+V f$3EfW^4f0,Kټef+✔7LEUIڹ5moǚaTGvg~+v+ BJuH+/̷NB LӔdu*7ɭ\.)aOdǺ'R W$[Ud}dIᆉwz3?T*լtME-UE;̍ -殕6Ywf_>|9?sWH3:/A3{&OȞxsPiZ CJl ")??VH~r (>xݟ*@r̺67%;w3h"YnTE%a"Ӻ/rYӺ4u^ۺJVc" 0)3{1lgff][ch>OS 0+Vz ;E;&ٱf6{61lX笧ױRut$x!Kى̋ЛfG$v3Pm聿AVOfbme*tmBwTz`lCcӟ{'Tl"oM+Xw {za+7mCͪ"=B߲&Gr*qR,Vq.!9iol3zS˧ɾ/YXkO9}#M~$Za*U(ROKyV,Flgb| do''Q~=wo}h@HyA !JHTz`Pf6& ELt#zwF[fWZ#UpFOC]@H]vH_DR%loMۙ!uH?\@pwORS:@wχ~d@>Kaf7ERZA+*"Nܘ^ ;Z`#]wS. +\jh2HCgBVt/`rgQqovJ49n \B cfk{"'jC.:<3wP f` 7 AnBI/=UJ?DzaKPv7Us5 [mjtZ-9+){Gٗ6Y T#CD_aYd{Q-U\"Vj g.՚bV쩐]=@ȺwL5/Cȕ"y. Ѓ=Tfd}cdK?cӸkw"/ ?K~̳iy}ʒy1(OX%2-NGŞopVH&-J{ezt/(F\i&ż'Ծ&R|U/p>F>4u;3;e"mmVRׄx\~;wj1Kw= }0VX,lf4Ӷ?sNC07AB2,2.Jgfk1bmbo3=ݽ /D3^ |; F plUT>6r(v&mYcк3*T:zZ޺:bf+LVB7uwgf7:,H$ T(FZW؞>k!Ш6qNWX+n 3;O07}P%v f0z.<6zxqH@2 Ѻ]$?CXn')LgC'qf6Sg|yS|zY?@e(OB1k#oݟ?¢49,PMvZ~(wg>CUs ',s!5WjZv&^ j!B5ßZ`p;3r|D>儉t3[Jm(Q\SB=( t (9h8bl7&?Bo0Uu؏oʂPoL2ڦb,~Nx6eVe+^$@&!UloZI$lM[ za( =V49+pT]-jt@ȟrU +{1rجfV&%bj$ٜ+\^1:[#i%1 w~ONn 2<4IJ ZV}̷rGڟ*iMr-s *֘ϛW gW%i+GPYYdm<17y2 5J3 1}pP,A1+.m?[-Y'aaSj rŔ~ɪX i n4=^BY̿:(UzM6Tï4R4r0Wr4҃vzV-Pby]ASr|OPjhX/CkN"aKfv}gڹn3DJy!\mhZ1+i%/;Sy|2Ϣ%1R5əxC䕑\)#8_f5XUN &;{S}M`?8=PDjx4zC>%ڜ:7=ElkX n IDAT'm? KIak]xk}n.3g_ʹ18_aE W[}=6ͭaGo$dۚ6FÚhc43,SU)h3Ԩ͵iWGlsbm4LOzTΪ(qQoϠiJ ͬ{/Psl):XuLV}90SuEJ}Qgi@?[Pp$6Bq;=wޕۗ+u:o&g,zPUDn _ۼ cz̮mאIc?=5Y%@ouǸ:Z3]3?Cg9Z'AT9Bz:4C`WM-p+ :k6YȷUw6YPG<8rKR:W9,n_=gG8#3:/|cPhꏩ}h|5 QbCbiwFtp 0ĕpT;==cOd\ju$FgQZ K=ኺ;f Ikh80}F ˧a.Ăl+Z_UaT;Tt~[f*!sz>zKy2k E 0#Rw I\Lc{ ڧhqN^X~1޹MW(vОJ2 2@5GF/Y);[VwiO>`ǡku8ƹx+lW|3#Uvz͞+7~k9r.:c4]w}R=Ѝ{rwk_Aڝ M ܄?ߡ7\QId=~ -3 e]ݳ-ms9{2-p :דg>?^aZ$l@B{^6gd;vrg&7"zᕍW~ȶhwALW%Ahv^oinCl,R #ճ(z~lYB$̌szBuާE? sǵ v&fկg k8y#ޠaf\YTFV `W"$3LHMy+K|Twȿe$Ɔ#kYfCi=p_=;Ȋ~ +9.DH$<ǻ62- Lz)AF 3;)Tyj 42͏?HU: Dӄ9v:GM&G!X+dʺ8puPGSF#Aݍn!(ȓx*Z~HH F0D(vҔr3 @pP#uQ7y. In/ ?+VtZPz+PmիO5l> B (S r_XHx 7if[5t hDpT9a4$VF"a< !08Enk٦ ݁  h&*aȒ0WPcA$,Gi*umAA0 W>'#T^dIF ̀-]$J 7D…f^#! N /"Ȓʁp-"(!Ҫ(eqWulFCAt%@_DhTiTtv47d=Dª(OB}X$@YW!x)(V /"awpYޘ3tcl>3{]ܥ0 w:@΅,$,|@WY6.ᆠꆌ¾L2ٔfgTj2fs$$ l _Dj EfE*`+ow.Y "aB0 }O$\4̦ wYLjGטr-h^&cPTH$n!kKאHb"|hqI_kc3A-} IJ  `$HShUZ|nTIvK ad`4Y5`^{fe -r:Y fDF CQ"Hu$2%!2~fV҉ܝѧ_vK7f60w?ke ;p=0=/\$>A~ %}JcfoTINi(f@5N :aӿtu_IV nh&{ "'c\u!fvbvAG, nTrg}K,wt:E=Pe7QR+Q4VE#Nݓ'EBDwYnz#KyH4,<-͏$ffGhw4{I螘 fvdf=3;u&3]B3{n7l*Q+fv]iff62C1[3if7قzI3CZ7܊fvofa*3;̞F03fl `}:3̆ٹ_Z6/qi1uY~t6lgo32l[31mffkf]jfTӠ[IG{V40]c3۷͞ 3kf1ׁ1f6>6ͬ%t1wPP?4BNK ³IezSE| oC{"YGb"g0]*(`lfE֝ۯv_04m̲z%Z r}2YV`\CPJ , OFVp"'GQ´;R;䖝e,l"5w<~H*0eZo2gg?#~ӹ?U㛖?+-y:qpm Hm2' Gƍi]ێO.@3}H;,D"a( 7qF}>`%Dd])Z8ЮM_ =M_X 7`ZV؏-27GU҃j2>3iri=͑2ӼJŴ:X?^t3!HTH{ }CH(\]-zn?sn} L7Tm|7g_UZ˝P`- AE B"뮎yYf4y=+~h }=tʲx7]An~~])\mnt_sG-7 i̍u yZg$02힝CznheCJkhZ)p`}BI}XGNGEg3_nkfYow/vs \Ee3i|?k*mGPE/H+uH6̱mhq}Gl-} Eȉx9h2-|R=N@VS,R65IFKo٩627>&_FrPm}ȴXX̞+!?N0E ,efo&(dxqwO ~N'W5tb nffu'mg'O@!)E+$G{Rod;k1ʰ7FcT A} I$DBu2p",ތ^Go'6cmf6sjw4W>*yPߌ砇VMYtwFS̎ȵjf_13lNڝ ;w? - NgCI dKk'L[ >,SV ܽQ-RLVlb2_[Xݯw. 2y/M޳DHa ȋJe3F8}ӐOԒ;VU wF!^hB +lavCn G"!K p ;gH3>2=ef/Y}S̬̀E;`p}S0Gfhy*v6EhO=YhW(AFY0݋v R,ҹcRd90w{f}SEͫ3-os[PHac6+;?Ǻ.hXd E@WsE5B͎zKJ 6Z swC@+G񳶳iL#,N"67-$N-LXZ!WHǼ0dLA(aهiv-9g#A-=ӧWZ wS%F͂5ݱhXw2 wlk$OA# ЛȁgM#0# :+N*13~%!-8#cgt %BlݍnBM\)}֟|F/{$+!("ovf炠@>=8<* BzˠbpY̶Ao(lŌH8-DBt)7R83{=̞F ˺S] #PGlAx8p *$%} .BRWCaqSS'GOBt )t(mN[60ċT L:7=]| C_=}Mviz ӈ!:'}J8st6%?YYB M7S~E}l ݐOBT J/g!]PnQzQRfwZCw,d= pFPR[l]lf 9 yNkq-fD굽 I u< Q3QճP(ЃHVB> Y$NJfI^6-$& U&\mAON.Aowb.["h`WvgpqǙRI$l"! !kG(Lw<|a;AA7 B'"N!Ks(Tq(rJ| _*1[H2_ifGC-Uvo^7虦) pWFAMɸ9(' x0j3L7!.7" `f6 UI M},}U;At+"0ߣo{ȁyȯkp 7\U)-F!ƛٔ(Hw̾l7Y6[*4\ALĄpѐÖ(({O1R8E7"6N`v (>C5(qS ~f&M I t1f6Jt p*+xlYL_Y6r f"df # 3.rhgs Q2 4rf6J}fQ{ACXw \ Cy $VN5Ɂ8̊JE2Nb$0Q%`$EC* qA0&6@(9"Waq G7,( pnf롰ϷK#c3!m:f$d I 4 ~'r<Op,Yq0Y ~Ű:po I 4)1(EhT^hu$"By."441-/G8EQ] &-‚d =C;ρ CP QmgIl<|0w=?D96ȉŀCALI15Q(|+$ CQ cbˎ. IDATT2Rpiw4NH/ͅJGoF!EQ~|2;m #f,n,OQhW(( hazTqw Eɕ6 A&1MHiKsf0_M IX D`<0-0m  6m}H+ hN‚D$À3[| ^z y:f, If6u>+l]T B ` BafW#rB|ؚ"?Ƹ? q\0Qp~}eig$Dsj {IH#U݊,-9XYEf6uqgEKM?hxw= l}jHdi8 3~ ̉ʊw<'.XFǡ0S, 讄 0;'`ifPP;P(4(Rgȧ`lb#]7(ZfvT$qU_\8{rAjǁPDOTˀl 'E  t1f OY'p%pWvE.F_Er1?#*OT4jA4aAzV+ŁMofBJ[ĵji;_aQeY6s`9`+$  4f6'mzYoyn{ p7kx 3^r}lc }38MAI&zkq(sM~mx L8)auv`w G3[ xzX8]sX'8:M0< hϣ"Q%$mT[Qρ P6ȵQeAЍ,P)SlJ?K~@ΕAA7CO`G٪`JD/3g40 U|4 9F At3!(AQ3 ǡaatZn%bw,JUNͭńEAФ@%EV eM\?1_"B?|`Z$ f[!(y]AAՄ@JJ< e^<Ux| fkvA7 :>AI 3(3~ `fS] "!( w19%|gfmAi(Ym[ b|ERAwvl;Tmr.w 'BP6f,ugzuȪ-rTx8A5>A%, @E eFdK{] v‚TD`_. It also supports JSON Web Signatures (JWS). The library is BSD licensed. Installing ---------- Install and update using `pip`_: .. code-block:: text pip install -U itsdangerous .. _pip: https://pip.pypa.io/en/stable/quickstart/ Example Use Cases ----------------- - You can serialize and sign a user ID in a URL and email it to them to unsubscribe from a newsletter. This way you don't need to generate one-time tokens and store them in the database. Same thing with any kind of activation link for accounts and similar things. - Signed objects can be stored in cookies or other untrusted sources which means you don't need to have sessions stored on the server, which reduces the number of necessary database queries. - Signed information can safely do a roundtrip between server and client in general which makes them useful for passing server-side state to a client and then back. Table of Contents ----------------- .. toctree:: signer serializer exceptions timed url_safe jws encoding license changes itsdangerous-1.1.0/docs/jws.rst0000644000175000017500000000271213364710266016717 0ustar daviddavid00000000000000.. module:: itsdangerous.jws JSON Web Signature (JWS) ======================== JSON Web Signatures (JWS) work similarly to the existing URL safe serializer but will emit headers according to `draft-ietf-jose-json-web -signature `_. .. 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: itsdangerous-1.1.0/docs/license.rst0000644000175000017500000000005513364710266017534 0ustar daviddavid00000000000000License ======= .. include:: ../LICENSE.rst itsdangerous-1.1.0/docs/make.bat0000644000175000017500000000136013364710266016765 0ustar daviddavid00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd itsdangerous-1.1.0/docs/requirements.txt0000644000175000017500000000005313364710266020642 0ustar daviddavid00000000000000Sphinx~=1.8.0 Pallets-Sphinx-Themes~=1.1.0 itsdangerous-1.1.0/docs/serializer.rst0000644000175000017500000001025313364710266020264 0ustar daviddavid00000000000000.. module:: itsdangerous.serializer Serialization Interface ======================= The :doc:`/signer` only signs strings. To sign other types, the :class:`Serializer` class provides a ``dumps``/``loads`` interface similar to Python's :mod:`json` module, which serializes the object to a string then signs that. Use :meth:`~Serializer.dumps` to serialize and sign the data: .. code-block:: python from itsdangerous.serializer import Serializer s = Serializer("secret-key") s.dumps([1, 2, 3, 4]) b'[1, 2, 3, 4].r7R9RhGgDPvvWl3iNzLuIIfELmo' Use :meth:`~Serializer.loads` to verify the signature and deserialize the data. .. code-block:: python s.loads('[1, 2, 3, 4].r7R9RhGgDPvvWl3iNzLuIIfELmo') [1, 2, 3, 4] By default, data is serialized to JSON. If simplejson is installed, it is preferred over the built-in :mod:`json` module. This internal serializer can be changed by subclassing. To record and validate the age of the signature, see :doc:`/timed`. To serialize to a format that is safe to use in URLs, see :doc:`/url_safe`. .. _the-salt: The Salt -------- All classes also accept a salt argument. The name might be misleading because usually if you think of salts in cryptography you would expect the salt to be something that is stored alongside the resulting signed string as a way to prevent rainbow table lookups. Such salts are usually public. In itsdangerous, like in the original Django implementation, the salt serves a different purpose. You could describe it as namespacing. It's still not critical if you disclose it because without the secret key it does not help an attacker. Let's assume that you have two links you want to sign. You have the activation link on your system which can activate a user account and you have an upgrade link that can upgrade a user's account to a paid account which you send out via email. If in both cases all you sign is the user ID a user could reuse the variable part in the URL from the activation link to upgrade the account. Now you could either put more information in there which you sign (like the intention: upgrade or activate), but you could also use different salts: .. code-block:: python from itsdangerous.url_safe import URLSafeSerializer s1 = URLSafeSerializer("secret-key", salt="activate") s1.dumps(42) 'NDI.MHQqszw6Wc81wOBQszCrEE_RlzY' s2 = URLSafeSerializer("secret-key", salt="upgrade") s2.dumps(42) 'NDI.c0MpsD6gzpilOAeUPra3NShPXsE' The second serializer can't load data dumped with the first because the salts differ: .. code-block:: python s2.loads(s1.dumps(42)) Traceback (most recent call last): ... itsdangerous.exc.BadSignature: Signature "MHQqszw6Wc81wOBQszCrEE_RlzY" does not match Only the serializer with the same salt can load the data: .. code-block:: python s2.loads(s2.dumps(42)) 42 Responding to Failure --------------------- Exceptions have helpful attributes which allow you to inspect the payload if the signature check failed. This has to be done with extra care because at that point you know that someone tampered with your data but it might be useful for debugging purposes. .. code-block:: python from itsdangerous.serializer import Serializer from itsdangerous.exc import BadSignature, BadData s = URLSafeSerializer("secret-key") decoded_payload = None try: decoded_payload = s.loads(data) # This payload is decoded and safe except BadSignature as e: if e.payload is not None: try: decoded_payload = s.load_payload(e.payload) except BadData: pass # This payload is decoded but unsafe because someone # tampered with the signature. The decode (load_payload) # step is explicit because it might be unsafe to unserialize # the payload (think pickle instead of json!) If you don't want to inspect attributes to figure out what exactly went wrong you can also use :meth:`~Serializer.loads_unsafe`: .. code-block:: python sig_okay, payload = s.loads_unsafe(data) The first item in the returned tuple is a boolean that indicates if the signature was correct. API --- .. autoclass:: Serializer :members: itsdangerous-1.1.0/docs/signer.rst0000644000175000017500000000243613364710266017406 0ustar daviddavid00000000000000.. module:: itsdangerous.signer Signing Interface ================= The most basic interface is the signing interface. The :class:`Signer` class can be used to attach a signature to a specific string: .. code-block:: python from itsdangerous import Signer s = Signer("secret-key") s.sign("my string") b'my string.wh6tMHxLgJqB6oY1uT73iMlyrOA' The signature is appended to the string, separated by a dot. To validate the string, use the :meth:`~Signer.unsign` method: .. code-block:: python s.unsign(b"my string.wh6tMHxLgJqB6oY1uT73iMlyrOA") b'my string' If unicode strings are provided, an implicit encoding to UTF-8 happens. However after unsigning you won't be able to tell if it was unicode or a bytestring. If the value is changed, the signature will no longer match, and unsigning will raise a :exc:`~itsdangerous.exc.BadSignature` exception: .. code-block:: python s.unsign(b"different string.wh6tMHxLgJqB6oY1uT73iMlyrOA") Traceback (most recent call last): ... itsdangerous.exc.BadSignature: Signature "wh6tMHxLgJqB6oY1uT73iMlyrOA" does not match To record and validate the age of a signature, see :doc:`/timed`. .. autoclass:: Signer :members: Signing Algorithms ------------------ .. autoclass:: NoneAlgorithm .. autoclass:: HMACAlgorithm itsdangerous-1.1.0/docs/timed.rst0000644000175000017500000000124713364710266017220 0ustar daviddavid00000000000000.. module:: itsdangerous.timed Signing With Timestamps ======================= If you want to expire signatures you can use the :class:`TimestampSigner` class which will adds timestamp information and signs it. On unsigning you can validate that the timestamp did not expire: .. code-block:: python from itsdangerous import TimestampSigner s = TimestampSigner('secret-key') string = s.sign('foo') .. code-block:: python s.unsign(string, max_age=5) Traceback (most recent call last): ... itsdangerous.exc.SignatureExpired: Signature age 15 > 5 seconds .. autoclass:: TimestampSigner :members: .. autoclass:: TimedSerializer :members: itsdangerous-1.1.0/docs/url_safe.rst0000644000175000017500000000114713364710266017715 0ustar daviddavid00000000000000.. module:: itsdangerous.url_safe URL Safe Serialization ====================== Often it is helpful if you can pass these trusted strings in places where you only have a limited set of characters available. Because of this, itsdangerous also provides URL safe serializers: .. code-block:: python from itsdangerous.url_safe import URLSafeSerializer s = URLSafeSerializer("secret-key") s.dumps([1, 2, 3, 4]) 'WzEsMiwzLDRd.wSPHqC0gR7VUqivlSukJ0IeTDgo' s.loads("WzEsMiwzLDRd.wSPHqC0gR7VUqivlSukJ0IeTDgo") [1, 2, 3, 4] .. autoclass:: URLSafeSerializer .. autoclass:: URLSafeTimedSerializer itsdangerous-1.1.0/setup.cfg0000644000175000017500000000070013364726771016256 0ustar daviddavid00000000000000[metadata] license_file = LICENSE.rst [bdist_wheel] universal = 1 [tool:pytest] testpaths = tests [coverage:run] branch = True source = itsdangerous tests [coverage:paths] source = src/itsdangerous .tox/*/lib/python*/site-packages/itsdangerous .tox/*/site-packages/itsdangerous [flake8] select = B, E, F, W, B9 ignore = E203, E501, W503 max-line-length = 80 exclude = src/itsdangerous/__init__.py [egg_info] tag_build = tag_date = 0 itsdangerous-1.1.0/setup.py0000644000175000017500000000325113364715667016155 0ustar daviddavid00000000000000import io import re from setuptools import find_packages from setuptools import setup with io.open("README.rst", "rt", encoding="utf8") as f: readme = f.read() with io.open("src/itsdangerous/__init__.py", "rt", encoding="utf8") as f: version = re.search(r"__version__ = \"(.*?)\"", f.read()).group(1) setup( name="itsdangerous", version=version, url="https://palletsprojects.com/p/itsdangerous/", project_urls={ "Documentation": "https://itsdangerous.palletsprojects.com/", "Code": "https://github.com/pallets/itsdangerous", "Issue tracker": "https://github.com/pallets/itsdangerous/issues", }, license="BSD", author="Armin Ronacher", author_email="armin.ronacher@active-4.com", maintainer="Pallets Team", maintainer_email="contact@palletsprojects.com", description="Various helpers to pass data to untrusted environments and back.", long_description=readme, packages=find_packages("src"), package_dir={"": "src"}, include_package_data=True, python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", ], ) itsdangerous-1.1.0/src/0000755000175000017500000000000013364726771015227 5ustar daviddavid00000000000000itsdangerous-1.1.0/src/itsdangerous/0000755000175000017500000000000013364726771017736 5ustar daviddavid00000000000000itsdangerous-1.1.0/src/itsdangerous/__init__.py0000644000175000017500000000130413364726423022037 0ustar daviddavid00000000000000from ._json import json 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 BadTimeSignature from .exc import SignatureExpired from .jws import JSONWebSignatureSerializer from .jws import TimedJSONWebSignatureSerializer from .serializer import Serializer from .signer import HMACAlgorithm from .signer import NoneAlgorithm from .signer import Signer from .timed import TimedSerializer from .timed import TimestampSigner from .url_safe import URLSafeSerializer from .url_safe import URLSafeTimedSerializer __version__ = "1.1.0" itsdangerous-1.1.0/src/itsdangerous/_compat.py0000644000175000017500000000215513364710266021725 0ustar daviddavid00000000000000import decimal import hmac import numbers import sys PY2 = sys.version_info[0] == 2 if PY2: from itertools import izip text_type = unicode # noqa: 821 else: izip = zip text_type = str number_types = (numbers.Real, decimal.Decimal) def _constant_time_compare(val1, val2): """Return ``True`` if the two strings are equal, ``False`` otherwise. The time taken is independent of the number of characters that match. Do not use this function for anything else than comparision with known length targets. This is should be implemented in C in order to get it completely right. This is an alias of :func:`hmac.compare_digest` on Python>=2.7,3.3. """ len_eq = len(val1) == len(val2) if len_eq: result = 0 left = val1 else: result = 1 left = val2 for x, y in izip(bytearray(left), bytearray(val2)): result |= x ^ y return result == 0 # Starting with 2.7/3.3 the standard library has a c-implementation for # constant time string compares. constant_time_compare = getattr(hmac, "compare_digest", _constant_time_compare) itsdangerous-1.1.0/src/itsdangerous/_json.py0000644000175000017500000000065713364710266021420 0ustar daviddavid00000000000000try: import simplejson as json except ImportError: import json class _CompactJSON(object): """Wrapper around json module that strips whitespace.""" @staticmethod def loads(payload): return json.loads(payload) @staticmethod def dumps(obj, **kwargs): kwargs.setdefault("ensure_ascii", False) kwargs.setdefault("separators", (",", ":")) return json.dumps(obj, **kwargs) itsdangerous-1.1.0/src/itsdangerous/encoding.py0000644000175000017500000000231013364710266022062 0ustar daviddavid00000000000000import base64 import string import struct from ._compat import text_type from .exc import BadData def want_bytes(s, encoding="utf-8", errors="strict"): if isinstance(s, text_type): s = s.encode(encoding, errors) return s def base64_encode(string): """Base64 encode a string of bytes or text. The resulting bytes are safe to use in URLs. """ string = want_bytes(string) return base64.urlsafe_b64encode(string).rstrip(b"=") def base64_decode(string): """Base64 decode a URL-safe string of bytes or text. The result is bytes. """ string = want_bytes(string, encoding="ascii", errors="ignore") string += b"=" * (-len(string) % 4) try: return base64.urlsafe_b64decode(string) except (TypeError, ValueError): raise BadData("Invalid base64-encoded data") # The alphabet used by base64.urlsafe_* _base64_alphabet = (string.ascii_letters + string.digits + "-_=").encode("ascii") _int64_struct = struct.Struct(">Q") _int_to_bytes = _int64_struct.pack _bytes_to_int = _int64_struct.unpack def int_to_bytes(num): return _int_to_bytes(num).lstrip(b"\x00") def bytes_to_int(bytestr): return _bytes_to_int(bytestr.rjust(8, b"\x00"))[0] itsdangerous-1.1.0/src/itsdangerous/exc.py0000644000175000017500000000561713364710266021070 0ustar daviddavid00000000000000from ._compat import PY2 from ._compat import text_type class BadData(Exception): """Raised if bad data of any sort was encountered. This is the base for all exceptions that itsdangerous defines. .. versionadded:: 0.15 """ message = None def __init__(self, message): super(BadData, self).__init__(self, message) self.message = message def __str__(self): return text_type(self.message) if PY2: __unicode__ = __str__ def __str__(self): return self.__unicode__().encode("utf-8") class BadSignature(BadData): """Raised if a signature does not match.""" def __init__(self, message, payload=None): BadData.__init__(self, message) #: The payload that failed the signature test. In some #: situations you might still want to inspect this, even if #: you know it was tampered with. #: #: .. versionadded:: 0.14 self.payload = payload class BadTimeSignature(BadSignature): """Raised if a time-based signature is invalid. This is a subclass of :class:`BadSignature`. """ def __init__(self, message, payload=None, date_signed=None): BadSignature.__init__(self, message, payload) #: If the signature expired this exposes the date of when the #: signature was created. This can be helpful in order to #: tell the user how long a link has been gone stale. #: #: .. versionadded:: 0.14 self.date_signed = date_signed class SignatureExpired(BadTimeSignature): """Raised if a signature timestamp is older than ``max_age``. This is a subclass of :exc:`BadTimeSignature`. """ class BadHeader(BadSignature): """Raised if a signed header is invalid in some form. This only happens for serializers that have a header that goes with the signature. .. versionadded:: 0.24 """ def __init__(self, message, payload=None, header=None, original_error=None): BadSignature.__init__(self, message, payload) #: If the header is actually available but just malformed it #: might be stored here. self.header = header #: If available, the error that indicates why the payload was #: not valid. This might be ``None``. self.original_error = original_error class BadPayload(BadData): """Raised if a payload is invalid. This could happen if the payload is loaded despite an invalid signature, or if there is a mismatch between the serializer and deserializer. The original exception that occurred during loading is stored on as :attr:`original_error`. .. versionadded:: 0.15 """ def __init__(self, message, original_error=None): BadData.__init__(self, message) #: If available, the error that indicates why the payload was #: not valid. This might be ``None``. self.original_error = original_error itsdangerous-1.1.0/src/itsdangerous/jws.py0000644000175000017500000001645613364710266021117 0ustar daviddavid00000000000000import hashlib import time from datetime import datetime from ._compat import number_types from ._json import _CompactJSON from ._json import json 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. """ 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, ): Serializer.__init__( self, secret_key=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, ) 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, ) try: header = Serializer.load_payload(self, json_header, serializer=json) except BadData as e: raise BadHeader( "Could not unserialize header because it was malformed", original_error=e, ) if not isinstance(header, dict): raise BadHeader("Header payload is not a JSON object", header=header) payload = Serializer.load_payload(self, 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") 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_key, 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 `_. """ DEFAULT_EXPIRES_IN = 3600 def __init__(self, secret_key, expires_in=None, **kwargs): JSONWebSignatureSerializer.__init__(self, 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 = JSONWebSignatureSerializer.make_header(self, 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 = JSONWebSignatureSerializer.loads( self, 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: raise int_date_error 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): rv = header.get("iat") if isinstance(rv, number_types): return datetime.utcfromtimestamp(int(rv)) def now(self): return int(time.time()) itsdangerous-1.1.0/src/itsdangerous/serializer.py0000644000175000017500000002071513364722635022461 0ustar daviddavid00000000000000import hashlib from ._compat import text_type from ._json import json from .encoding import want_bytes from .exc import BadPayload from .exc import BadSignature from .signer import Signer def is_text_serializer(serializer): """Checks whether a serializer generates text or binary.""" return isinstance(serializer.dumps({}), text_type) class Serializer(object): """This class provides a serialization interface on top of the signer. It provides a similar API to json/pickle and other modules but is structured differently internally. If you want to change the underlying implementation for parsing and loading you have to override the :meth:`load_payload` and :meth:`dump_payload` functions. This implementation uses simplejson if available for dumping and loading and will fall back to the standard library's json module if it's not available. You do not need to subclass this class in order to switch out or customize the :class:`.Signer`. You can instead pass a different class to the constructor as well as keyword arguments as a dict that should be forwarded. .. code-block:: python s = Serializer(signer_kwargs={'key_derivation': 'hmac'}) You may want to upgrade the signing parameters without invalidating existing signatures that are in use. Fallback signatures can be given that will be tried if unsigning with the current signer fails. Fallback signers can be defined by providing a list of ``fallback_signers``. Each item can be one of the following: a signer class (which is instantiated with ``signer_kwargs``, ``salt``, and ``secret_key``), a tuple ``(signer_class, signer_kwargs)``, or a dict of ``signer_kwargs``. For example, this is a serializer that signs using SHA-512, but will unsign using either SHA-512 or SHA1: .. code-block:: python s = Serializer( signer_kwargs={"digest_method": hashlib.sha512}, fallback_signers=[{"digest_method": hashlib.sha1}] ) .. versionchanged:: 0.14: The ``signer`` and ``signer_kwargs`` parameters were added to the constructor. .. versionchanged:: 1.1.0: 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. """ #: If a serializer module or class is not passed to the constructor #: this one is picked up. This currently defaults to :mod:`json`. default_serializer = json #: The default :class:`Signer` class that is being used by this #: serializer. #: #: .. versionadded:: 0.14 default_signer = Signer #: The default fallback signers. default_fallback_signers = [{"digest_method": hashlib.sha512}] def __init__( self, secret_key, salt=b"itsdangerous", serializer=None, serializer_kwargs=None, signer=None, signer_kwargs=None, fallback_signers=None, ): self.secret_key = want_bytes(secret_key) self.salt = want_bytes(salt) if serializer is None: serializer = self.default_serializer self.serializer = serializer self.is_text_serializer = is_text_serializer(serializer) if signer is None: signer = self.default_signer self.signer = signer self.signer_kwargs = signer_kwargs or {} if fallback_signers is None: fallback_signers = list(self.default_fallback_signers or ()) self.fallback_signers = fallback_signers self.serializer_kwargs = serializer_kwargs or {} def load_payload(self, payload, serializer=None): """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: payload = 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, ) def dump_payload(self, obj): """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=None): """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_key, salt=salt, **self.signer_kwargs) def iter_unsigners(self, salt=None): """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 type(fallback) is dict: kwargs = fallback fallback = self.signer elif type(fallback) is tuple: fallback, kwargs = fallback else: kwargs = self.signer_kwargs yield fallback(self.secret_key, salt=salt, **kwargs) def dumps(self, obj, salt=None): """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: rv = rv.decode("utf-8") return rv def dump(self, obj, f, salt=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, salt=None): """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 last_exception def load(self, f, salt=None): """Like :meth:`loads` but loads from a file.""" return self.loads(f.read(), salt) def loads_unsafe(self, s, salt=None): """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, salt, load_kwargs=None, load_payload_kwargs=None): """Low level helper function to implement :meth:`loads_unsafe` in serializer subclasses. """ try: return True, self.loads(s, salt=salt, **(load_kwargs or {})) except BadSignature as e: if e.payload is None: return False, None try: return ( False, self.load_payload(e.payload, **(load_payload_kwargs or {})), ) except BadPayload: return False, None def load_unsafe(self, f, *args, **kwargs): """Like :meth:`loads_unsafe` but loads from a file. .. versionadded:: 0.15 """ return self.loads_unsafe(f.read(), *args, **kwargs) itsdangerous-1.1.0/src/itsdangerous/signer.py0000644000175000017500000001431113364715667021601 0ustar daviddavid00000000000000import hashlib import hmac from ._compat import constant_time_compare from .encoding import _base64_alphabet from .encoding import base64_decode from .encoding import base64_encode from .encoding import want_bytes from .exc import BadSignature class SigningAlgorithm(object): """Subclasses must implement :meth:`get_signature` to provide signature generation functionality. """ def get_signature(self, key, value): """Returns the signature for the given key and value.""" raise NotImplementedError() def verify_signature(self, key, value, sig): """Verifies the given signature matches the expected signature. """ return constant_time_compare(sig, self.get_signature(key, value)) class NoneAlgorithm(SigningAlgorithm): """Provides an algorithm that does not perform any signing and returns an empty signature. """ def get_signature(self, key, value): return b"" class HMACAlgorithm(SigningAlgorithm): """Provides signature generation using HMACs.""" #: The digest method to use with the MAC algorithm. This defaults to #: SHA1, but can be changed to any other function in the hashlib #: module. default_digest_method = staticmethod(hashlib.sha1) def __init__(self, digest_method=None): if digest_method is None: digest_method = self.default_digest_method self.digest_method = digest_method def get_signature(self, key, value): mac = hmac.new(key, msg=value, digestmod=self.digest_method) return mac.digest() class Signer(object): """This class can sign and unsign bytes, validating the signature provided. Salt can be used to namespace the hash, so that a signed string is only valid for a given namespace. Leaving this at the default value or re-using a salt value across different parts of your application where the same signed value in one part can mean something different in another part is a security risk. See :ref:`the-salt` for an example of what the salt is doing and how you can utilize it. .. versionadded:: 0.14 ``key_derivation`` and ``digest_method`` were added as arguments to the class constructor. .. versionadded:: 0.18 ``algorithm`` was added as an argument to the class constructor. """ #: The digest method to use for the signer. This defaults to #: SHA1 but can be changed to any other function in the hashlib #: module. #: #: .. versionadded:: 0.14 default_digest_method = staticmethod(hashlib.sha1) #: Controls how the key is derived. The default is Django-style #: concatenation. Possible values are ``concat``, ``django-concat`` #: and ``hmac``. This is used for deriving a key from the secret key #: with an added salt. #: #: .. versionadded:: 0.14 default_key_derivation = "django-concat" def __init__( self, secret_key, salt=None, sep=".", key_derivation=None, digest_method=None, algorithm=None, ): self.secret_key = want_bytes(secret_key) self.sep = want_bytes(sep) if self.sep in _base64_alphabet: raise ValueError( "The given separator cannot be used because it may be" " contained in the signature itself. Alphanumeric" " characters and `-_=` must not be used." ) self.salt = "itsdangerous.Signer" if salt is None else salt if key_derivation is None: key_derivation = self.default_key_derivation self.key_derivation = key_derivation if digest_method is None: digest_method = self.default_digest_method self.digest_method = digest_method if algorithm is None: algorithm = HMACAlgorithm(self.digest_method) self.algorithm = algorithm def derive_key(self): """This method is called to derive the key. The default key derivation choices can be overridden here. Key derivation is not intended to be used as a security method to make a complex key out of a short password. Instead you should use large random secret keys. """ salt = want_bytes(self.salt) if self.key_derivation == "concat": return self.digest_method(salt + self.secret_key).digest() elif self.key_derivation == "django-concat": return self.digest_method(salt + b"signer" + self.secret_key).digest() elif self.key_derivation == "hmac": mac = hmac.new(self.secret_key, digestmod=self.digest_method) mac.update(salt) return mac.digest() elif self.key_derivation == "none": return self.secret_key else: raise TypeError("Unknown key derivation method") def get_signature(self, value): """Returns the signature for the given value.""" value = want_bytes(value) key = self.derive_key() sig = self.algorithm.get_signature(key, value) return base64_encode(sig) def sign(self, value): """Signs the given string.""" return want_bytes(value) + want_bytes(self.sep) + self.get_signature(value) def verify_signature(self, value, sig): """Verifies the signature for the given value.""" key = self.derive_key() try: sig = base64_decode(sig) except Exception: return False return self.algorithm.verify_signature(key, value, sig) def unsign(self, signed_value): """Unsigns the given string.""" signed_value = want_bytes(signed_value) sep = want_bytes(self.sep) if sep not in signed_value: raise BadSignature("No %r found in value" % self.sep) value, sig = signed_value.rsplit(sep, 1) if self.verify_signature(value, sig): return value raise BadSignature("Signature %r does not match" % sig, payload=value) def validate(self, signed_value): """Only validates the given signed value. Returns ``True`` if the signature exists and is valid. """ try: self.unsign(signed_value) return True except BadSignature: return False itsdangerous-1.1.0/src/itsdangerous/timed.py0000644000175000017500000001300313364722330021372 0ustar daviddavid00000000000000import time from datetime import datetime from ._compat import text_type from .encoding import base64_decode from .encoding import base64_encode from .encoding import bytes_to_int from .encoding import int_to_bytes from .encoding import want_bytes from .exc import BadSignature from .exc import BadTimeSignature from .exc import SignatureExpired from .serializer import Serializer from .signer import Signer class TimestampSigner(Signer): """Works like the regular :class:`.Signer` but also records the time of the signing and can be used to expire signatures. The :meth:`unsign` method can raise :exc:`.SignatureExpired` if the unsigning failed because the signature is expired. """ def get_timestamp(self): """Returns the current timestamp. The function must return an integer. """ return int(time.time()) def timestamp_to_datetime(self, ts): """Used to convert the timestamp from :meth:`get_timestamp` into a datetime object. """ return datetime.utcfromtimestamp(ts) def sign(self, value): """Signs the given string and also attaches time information.""" value = want_bytes(value) timestamp = base64_encode(int_to_bytes(self.get_timestamp())) sep = want_bytes(self.sep) value = value + sep + timestamp return value + sep + self.get_signature(value) def unsign(self, value, max_age=None, return_timestamp=False): """Works like the regular :meth:`.Signer.unsign` but can also validate the time. See the base docstring of the class for the general behavior. If ``return_timestamp`` is ``True`` the timestamp of the signature will be returned as a naive :class:`datetime.datetime` object in UTC. """ try: result = Signer.unsign(self, value) sig_error = None except BadSignature as e: sig_error = e result = e.payload or b"" sep = want_bytes(self.sep) # If there is no timestamp in the result there is something # seriously wrong. In case there was a signature error, we raise # that one directly, otherwise we have a weird situation in # which we shouldn't have come except someone uses a time-based # serializer on non-timestamp data, so catch that. if sep not in result: if sig_error: raise sig_error raise BadTimeSignature("timestamp missing", payload=result) value, timestamp = result.rsplit(sep, 1) try: timestamp = bytes_to_int(base64_decode(timestamp)) except Exception: timestamp = None # Signature is *not* okay. Raise a proper error now that we have # split the value and the timestamp. if sig_error is not None: raise BadTimeSignature( text_type(sig_error), payload=value, date_signed=timestamp ) # Signature was okay but the timestamp is actually not there or # malformed. Should not happen, but we handle it anyway. if timestamp is None: raise BadTimeSignature("Malformed timestamp", payload=value) # Check timestamp is not older than max_age if max_age is not None: age = self.get_timestamp() - timestamp if age > max_age: raise SignatureExpired( "Signature age %s > %s seconds" % (age, max_age), payload=value, date_signed=self.timestamp_to_datetime(timestamp), ) if return_timestamp: return value, self.timestamp_to_datetime(timestamp) return value def validate(self, signed_value, max_age=None): """Only validates the given signed value. Returns ``True`` if the signature exists and is valid.""" try: self.unsign(signed_value, max_age=max_age) return True except BadSignature: return False class TimedSerializer(Serializer): """Uses :class:`TimestampSigner` instead of the default :class:`.Signer`. """ default_signer = TimestampSigner def loads(self, s, max_age=None, return_timestamp=False, salt=None): """Reverse of :meth:`dumps`, raises :exc:`.BadSignature` if the signature validation fails. If a ``max_age`` is provided it will ensure the signature is not older than that time in seconds. In case the signature is outdated, :exc:`.SignatureExpired` is raised. All arguments are forwarded to the signer's :meth:`~TimestampSigner.unsign` method. """ s = want_bytes(s) last_exception = None for signer in self.iter_unsigners(salt): try: base64d, timestamp = signer.unsign(s, max_age, return_timestamp=True) payload = self.load_payload(base64d) if return_timestamp: return payload, timestamp return payload # If we get a signature expired it means we could read the # signature but it's invalid. In that case we do not want to # try the next signer. except SignatureExpired: raise except BadSignature as err: last_exception = err raise last_exception def loads_unsafe(self, s, max_age=None, salt=None): load_kwargs = {"max_age": max_age} load_payload_kwargs = {} return self._loads_unsafe_impl(s, salt, load_kwargs, load_payload_kwargs) itsdangerous-1.1.0/src/itsdangerous/url_safe.py0000644000175000017500000000434313364710266022104 0ustar daviddavid00000000000000import zlib from ._json import _CompactJSON from .encoding import base64_decode from .encoding import base64_encode from .exc import BadPayload from .serializer import Serializer from .timed import TimedSerializer class URLSafeSerializerMixin(object): """Mixed in with a regular serializer it will attempt to zlib compress the string to make it shorter if necessary. It will also base64 encode the string so that it can safely be placed in a URL. """ default_serializer = _CompactJSON def load_payload(self, payload, *args, **kwargs): decompress = False if payload.startswith(b"."): payload = payload[1:] decompress = True try: json = base64_decode(payload) except Exception as e: raise BadPayload( "Could not base64 decode the payload because of an exception", original_error=e, ) if decompress: try: json = zlib.decompress(json) except Exception as e: raise BadPayload( "Could not zlib decompress the payload before decoding the payload", original_error=e, ) return super(URLSafeSerializerMixin, self).load_payload(json, *args, **kwargs) def dump_payload(self, obj): json = super(URLSafeSerializerMixin, self).dump_payload(obj) is_compressed = False compressed = zlib.compress(json) if len(compressed) < (len(json) - 1): json = compressed is_compressed = True base64d = base64_encode(json) if is_compressed: base64d = b"." + base64d return base64d class URLSafeSerializer(URLSafeSerializerMixin, Serializer): """Works like :class:`.Serializer` but dumps and loads into a URL safe string consisting of the upper and lowercase character of the alphabet as well as ``'_'``, ``'-'`` and ``'.'``. """ class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer): """Works like :class:`.TimedSerializer` but dumps and loads into a URL safe string consisting of the upper and lowercase character of the alphabet as well as ``'_'``, ``'-'`` and ``'.'``. """ itsdangerous-1.1.0/src/itsdangerous.egg-info/0000755000175000017500000000000013364726771021430 5ustar daviddavid00000000000000itsdangerous-1.1.0/src/itsdangerous.egg-info/PKG-INFO0000644000175000017500000000704113364726771022527 0ustar daviddavid00000000000000Metadata-Version: 1.2 Name: itsdangerous Version: 1.1.0 Summary: Various helpers to pass data to untrusted environments and back. Home-page: https://palletsprojects.com/p/itsdangerous/ Author: Armin Ronacher Author-email: armin.ronacher@active-4.com Maintainer: Pallets Team Maintainer-email: contact@palletsprojects.com License: BSD Project-URL: Documentation, https://itsdangerous.palletsprojects.com/ Project-URL: Code, https://github.com/pallets/itsdangerous Project-URL: Issue tracker, https://github.com/pallets/itsdangerous/issues Description: itsdangerous ============ ... so better sign this Various helpers to pass data to untrusted environments and to get it back safe and sound. Data is cryptographically signed to ensure that a token has not been tampered with. It's possible to customize how data is serialized. Data is compressed as needed. A timestamp can be added and verified automatically while loading a token. Installing ---------- Install and update using `pip`_: .. code-block:: text pip install -U itsdangerous .. _pip: https://pip.pypa.io/en/stable/quickstart/ A Simple Example ---------------- Here's how you could generate a token for transmitting a user's id and name between web requests. .. code-block:: python from itsdangerous import URLSafeSerializer auth_s = URLSafeSerializer("secret key", "auth") token = auth_s.dumps({"id": 5, "name": "itsdangerous"}) print(token) # eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg data = auth_s.loads(token) print(data["name"]) # itsdangerous Donate ------ The Pallets organization develops and supports itsdangerous and other popular packages. In order to grow the community of contributors and users, and allow the maintainers to devote more time to the projects, `please donate today`_. .. _please donate today: https://palletsprojects.com/donate Links ----- * Website: https://palletsprojects.com/p/itsdangerous/ * Documentation: https://itsdangerous.palletsprojects.com/ * License: `BSD `_ * Releases: https://pypi.org/project/itsdangerous/ * Code: https://github.com/pallets/itsdangerous * Issue tracker: https://github.com/pallets/itsdangerous/issues * Test status: https://travis-ci.org/pallets/itsdangerous * Test coverage: https://codecov.io/gh/pallets/itsdangerous Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* itsdangerous-1.1.0/src/itsdangerous.egg-info/SOURCES.txt0000644000175000017500000000215113364726771023313 0ustar daviddavid00000000000000CHANGES.rst LICENSE.rst MANIFEST.in README.rst setup.cfg setup.py tox.ini docs/Makefile docs/changes.rst docs/conf.py docs/encoding.rst docs/exceptions.rst docs/index.rst docs/jws.rst docs/license.rst docs/make.bat docs/requirements.txt docs/serializer.rst docs/signer.rst docs/timed.rst docs/url_safe.rst docs/_static/itsdangerous-logo-sidebar.png docs/_static/itsdangerous-logo.png src/itsdangerous/__init__.py src/itsdangerous/_compat.py src/itsdangerous/_json.py src/itsdangerous/encoding.py src/itsdangerous/exc.py src/itsdangerous/jws.py src/itsdangerous/serializer.py src/itsdangerous/signer.py src/itsdangerous/timed.py src/itsdangerous/url_safe.py src/itsdangerous.egg-info/PKG-INFO src/itsdangerous.egg-info/SOURCES.txt src/itsdangerous.egg-info/dependency_links.txt src/itsdangerous.egg-info/top_level.txt tests/test_itsdangerous/__init__.py tests/test_itsdangerous/test_compat.py tests/test_itsdangerous/test_encoding.py tests/test_itsdangerous/test_jws.py tests/test_itsdangerous/test_serializer.py tests/test_itsdangerous/test_signer.py tests/test_itsdangerous/test_timed.py tests/test_itsdangerous/test_url_safe.pyitsdangerous-1.1.0/src/itsdangerous.egg-info/dependency_links.txt0000644000175000017500000000000113364726771025476 0ustar daviddavid00000000000000 itsdangerous-1.1.0/src/itsdangerous.egg-info/top_level.txt0000644000175000017500000000001513364726771024156 0ustar daviddavid00000000000000itsdangerous itsdangerous-1.1.0/tests/0000755000175000017500000000000013364726771015602 5ustar daviddavid00000000000000itsdangerous-1.1.0/tests/test_itsdangerous/0000755000175000017500000000000013364726771021350 5ustar daviddavid00000000000000itsdangerous-1.1.0/tests/test_itsdangerous/__init__.py0000644000175000017500000000000013364710266023437 0ustar daviddavid00000000000000itsdangerous-1.1.0/tests/test_itsdangerous/test_compat.py0000644000175000017500000000045313364710266024236 0ustar daviddavid00000000000000import pytest from itsdangerous._compat import _constant_time_compare @pytest.mark.parametrize( ("a", "b", "expect"), ((b"a", b"a", True), (b"a", b"b", False), (b"a", b"aa", False)), ) def test_python_constant_time_compare(a, b, expect): assert _constant_time_compare(a, b) == expect itsdangerous-1.1.0/tests/test_itsdangerous/test_encoding.py0000644000175000017500000000200613364710266024535 0ustar daviddavid00000000000000# -*- coding: utf-8 -*- import pytest from itsdangerous.encoding import base64_decode from itsdangerous.encoding import base64_encode from itsdangerous.encoding import bytes_to_int from itsdangerous.encoding import int_to_bytes from itsdangerous.encoding import want_bytes from itsdangerous.exc import BadData @pytest.mark.parametrize("value", (u"mañana", b"tomorrow")) def test_want_bytes(value): out = want_bytes(value) assert isinstance(out, bytes) @pytest.mark.parametrize("value", (u"無限", b"infinite")) def test_base64(value): enc = base64_encode(value) assert isinstance(enc, bytes) dec = base64_decode(enc) assert dec == want_bytes(value) def test_base64_bad(): with pytest.raises(BadData): base64_decode("12345") @pytest.mark.parametrize( ("value", "expect"), ((0, b""), (192, b"\xc0"), (18446744073709551615, b"\xff" * 8)) ) def test_int_bytes(value, expect): enc = int_to_bytes(value) assert enc == expect dec = bytes_to_int(enc) assert dec == value itsdangerous-1.1.0/tests/test_itsdangerous/test_jws.py0000644000175000017500000001047013364722330023551 0ustar daviddavid00000000000000from datetime import timedelta from functools import partial import pytest from test_itsdangerous.test_serializer import TestSerializer from test_itsdangerous.test_timed import TestTimedSerializer 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 class TestJWSSerializer(TestSerializer): @pytest.fixture() def serializer_factory(self): return partial(JSONWebSignatureSerializer, secret_key="secret-key") test_signer_cls = None test_signer_kwargs = None test_fallback_signers = None test_iter_unsigners = None @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) class TestTimedJWSSerializer(TestJWSSerializer, TestTimedSerializer): @pytest.fixture() def serializer_factory(self): return partial( TimedJSONWebSignatureSerializer, secret_key="secret-key", expires_in=10 ) 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 itsdangerous-1.1.0/tests/test_itsdangerous/test_serializer.py0000644000175000017500000001412113364722330025114 0ustar daviddavid00000000000000import hashlib import pickle from functools import partial from io import BytesIO from io import StringIO import pytest from itsdangerous import Signer from itsdangerous.exc import BadPayload from itsdangerous.exc import BadSignature from itsdangerous.serializer import Serializer def coerce_str(ref, s): if not isinstance(s, type(ref)): return s.encode("utf8") return s class TestSerializer(object): @pytest.fixture(params=(Serializer, partial(Serializer, serializer=pickle))) def serializer_factory(self, request): return partial(request.param, secret_key="secret_key") @pytest.fixture() def serializer(self, serializer_factory): return serializer_factory() @pytest.fixture() def value(self): return {"id": 42} @pytest.mark.parametrize( "value", (None, True, "str", u"text", [1, 2, 3], {"id": 42}) ) def test_serializer(self, serializer, value): assert serializer.loads(serializer.dumps(value)) == value @pytest.mark.parametrize( "transform", ( lambda s: s.upper(), lambda s: s + coerce_str(s, "a"), lambda s: coerce_str(s, "a") + s[1:], lambda s: s.replace(coerce_str(s, "."), coerce_str(s, "")), ), ) def test_changed_value(self, serializer, value, transform): signed = serializer.dumps(value) assert serializer.loads(signed) == value changed = transform(signed) with pytest.raises(BadSignature): serializer.loads(changed) def test_bad_signature_exception(self, serializer, value): bad_signed = serializer.dumps(value)[:-1] with pytest.raises(BadSignature) as exc_info: serializer.loads(bad_signed) assert serializer.load_payload(exc_info.value.payload) == value def test_bad_payload_exception(self, serializer, value): original = serializer.dumps(value) payload = original.rsplit(coerce_str(original, "."), 1)[0] bad = serializer.make_signer().sign(payload[:-1]) with pytest.raises(BadPayload) as exc_info: serializer.loads(bad) assert exc_info.value.original_error is not None def test_loads_unsafe(self, serializer, value): signed = serializer.dumps(value) assert serializer.loads_unsafe(signed) == (True, value) bad_signed = signed[:-1] assert serializer.loads_unsafe(bad_signed) == (False, value) payload = signed.rsplit(coerce_str(signed, "."), 1)[0] bad_payload = serializer.make_signer().sign(payload[:-1])[:-1] assert serializer.loads_unsafe(bad_payload) == (False, None) class BadUnsign(serializer.signer): def unsign(self, signed_value, *args, **kwargs): try: return super(BadUnsign, self).unsign(signed_value, *args, **kwargs) except BadSignature as e: e.payload = None raise serializer.signer = BadUnsign assert serializer.loads_unsafe(bad_signed) == (False, None) def test_file(self, serializer, value): f = BytesIO() if isinstance(serializer.dumps(value), bytes) else StringIO() serializer.dump(value, f) f.seek(0) assert serializer.load(f) == value f.seek(0) assert serializer.load_unsafe(f) == (True, value) def test_alt_salt(self, serializer, value): signed = serializer.dumps(value, salt="other") with pytest.raises(BadSignature): serializer.loads(signed) assert serializer.loads(signed, salt="other") == value def test_signer_cls(self, serializer_factory, serializer, value): class Other(serializer.signer): default_key_derivation = "hmac" other = serializer_factory(signer=Other) assert other.loads(other.dumps(value)) == value assert other.dumps(value) != serializer.dumps(value) def test_signer_kwargs(self, serializer_factory, serializer, value): other = serializer_factory(signer_kwargs={"key_derivation": "hmac"}) assert other.loads(other.dumps(value)) == value assert other.dumps("value") != serializer.dumps("value") def test_serializer_kwargs(self, serializer_factory): serializer = serializer_factory(serializer_kwargs={"skipkeys": True}) try: serializer.serializer.dumps(None, skipkeys=True) except TypeError: return assert serializer.loads(serializer.dumps({(): 1})) == {} def test_fallback_signers(self, serializer_factory, value): serializer = serializer_factory(signer_kwargs={"digest_method": hashlib.sha256}) signed = serializer.dumps(value) fallback_serializer = serializer_factory( signer_kwargs={"digest_method": hashlib.sha1}, fallback_signers=[{"digest_method": hashlib.sha256}], ) assert fallback_serializer.loads(signed) == value def test_iter_unsigners(self, serializer, serializer_factory): class Signer256(serializer.signer): default_digest_method = hashlib.sha256 serializer = serializer_factory( secret_key="secret_key", fallback_signers=[ {"digest_method": hashlib.sha256}, (Signer, {"digest_method": hashlib.sha256}), Signer256, ], ) unsigners = serializer.iter_unsigners() assert next(unsigners).digest_method == hashlib.sha1 for signer in unsigners: assert signer.digest_method == hashlib.sha256 def test_digests(): factory = partial(Serializer, secret_key="dev key", salt="dev salt") default_value = factory(signer_kwargs={}).dumps([42]) sha1_value = factory(signer_kwargs={"digest_method": hashlib.sha1}).dumps([42]) sha512_value = factory(signer_kwargs={"digest_method": hashlib.sha512}).dumps([42]) assert default_value == sha1_value assert sha1_value == "[42].-9cNi0CxsSB3hZPNCe9a2eEs1ZM" assert sha512_value == ( "[42].MKCz_0nXQqv7wKpfHZcRtJRmpT2T5uvs9YQsJEhJimqxc" "9bCLxG31QzS5uC8OVBI1i6jyOLAFNoKaF5ckO9L5Q" ) itsdangerous-1.1.0/tests/test_itsdangerous/test_signer.py0000644000175000017500000000611513364710266024243 0ustar daviddavid00000000000000import hashlib from functools import partial import pytest from itsdangerous.exc import BadSignature from itsdangerous.signer import HMACAlgorithm from itsdangerous.signer import NoneAlgorithm from itsdangerous.signer import Signer from itsdangerous.signer import SigningAlgorithm class _ReverseAlgorithm(SigningAlgorithm): def get_signature(self, key, value): return (key + value)[::-1] class TestSigner(object): @pytest.fixture() def signer_factory(self): return partial(Signer, secret_key="secret-key") @pytest.fixture() def signer(self, signer_factory): return signer_factory() def test_signer(self, signer): signed = signer.sign("my string") assert isinstance(signed, bytes) assert signer.validate(signed) out = signer.unsign(signed) assert out == b"my string" def test_no_separator(self, signer): signed = signer.sign("my string") signed = signed.replace(signer.sep, b"*", 1) assert not signer.validate(signed) with pytest.raises(BadSignature): signer.unsign(signed) def test_broken_signature(self, signer): signed = signer.sign("b") bad_signed = signed[:-1] bad_sig = bad_signed.rsplit(b".", 1)[1] assert not signer.verify_signature(b"b", bad_sig) with pytest.raises(BadSignature) as exc_info: signer.unsign(bad_signed) assert exc_info.value.payload == b"b" def test_changed_value(self, signer): signed = signer.sign("my string") signed = signed.replace(b"my", b"other", 1) assert not signer.validate(signed) with pytest.raises(BadSignature): signer.unsign(signed) def test_invalid_separator(self, signer_factory): with pytest.raises(ValueError) as exc_info: signer_factory(sep="-") assert "separator cannot be used" in str(exc_info.value) @pytest.mark.parametrize( "key_derivation", ("concat", "django-concat", "hmac", "none") ) def test_key_derivation(self, signer_factory, key_derivation): signer = signer_factory(key_derivation=key_derivation) assert signer.unsign(signer.sign("value")) == b"value" def test_invalid_key_derivation(self, signer_factory): signer = signer_factory(key_derivation="invalid") with pytest.raises(TypeError): signer.derive_key() def test_digest_method(self, signer_factory): signer = signer_factory(digest_method=hashlib.md5) assert signer.unsign(signer.sign("value")) == b"value" @pytest.mark.parametrize( "algorithm", (None, NoneAlgorithm(), HMACAlgorithm(), _ReverseAlgorithm()) ) def test_algorithm(self, signer_factory, algorithm): signer = signer_factory(algorithm=algorithm) assert signer.unsign(signer.sign("value")) == b"value" if algorithm is None: assert signer.algorithm.digest_method == signer.digest_method def test_abstract_algorithm(): alg = SigningAlgorithm() with pytest.raises(NotImplementedError): alg.get_signature("a", "b") itsdangerous-1.1.0/tests/test_itsdangerous/test_timed.py0000644000175000017500000000531713364715721024062 0ustar daviddavid00000000000000from datetime import datetime from datetime import timedelta from functools import partial import pytest from freezegun import freeze_time from test_itsdangerous.test_serializer import TestSerializer from test_itsdangerous.test_signer import TestSigner from itsdangerous import Signer from itsdangerous.exc import BadTimeSignature from itsdangerous.exc import SignatureExpired from itsdangerous.timed import TimedSerializer from itsdangerous.timed import TimestampSigner class FreezeMixin(object): @pytest.fixture() def ts(self): return datetime(2011, 6, 24, 0, 9, 5) @pytest.fixture(autouse=True) def freeze(self, ts): with freeze_time(ts) as ft: yield ft class TestTimestampSigner(FreezeMixin, TestSigner): @pytest.fixture() def signer_factory(self): return partial(TimestampSigner, secret_key="secret-key") def test_max_age(self, signer, ts, freeze): signed = signer.sign("value") freeze.tick() assert signer.unsign(signed, max_age=10) == b"value" freeze.tick(timedelta(seconds=10)) with pytest.raises(SignatureExpired) as exc_info: signer.unsign(signed, max_age=10) assert exc_info.value.date_signed == ts def test_return_timestamp(self, signer, ts): signed = signer.sign("value") assert signer.unsign(signed, return_timestamp=True) == (b"value", ts) def test_timestamp_missing(self, signer): other = Signer("secret-key") signed = other.sign("value") with pytest.raises(BadTimeSignature) as exc_info: signer.unsign(signed) assert "missing" in str(exc_info.value) def test_malformed_timestamp(self, signer): other = Signer("secret-key") signed = other.sign(b"value.____________") with pytest.raises(BadTimeSignature) as exc_info: signer.unsign(signed) assert "Malformed" in str(exc_info.value) class TestTimedSerializer(FreezeMixin, TestSerializer): @pytest.fixture() def serializer_factory(self): return partial(TimedSerializer, secret_key="secret_key") def test_max_age(self, serializer, value, ts, freeze): signed = serializer.dumps(value) freeze.tick() assert serializer.loads(signed, max_age=10) == value freeze.tick(timedelta(seconds=10)) with pytest.raises(SignatureExpired) as exc_info: serializer.loads(signed, max_age=10) assert exc_info.value.date_signed == ts assert serializer.load_payload(exc_info.value.payload) == value def test_return_payload(self, serializer, value, ts): signed = serializer.dumps(value) assert serializer.loads(signed, return_timestamp=True) == (value, ts) itsdangerous-1.1.0/tests/test_itsdangerous/test_url_safe.py0000644000175000017500000000140713364710266024553 0ustar daviddavid00000000000000from functools import partial import pytest from test_itsdangerous.test_serializer import TestSerializer from test_itsdangerous.test_timed import TestTimedSerializer from itsdangerous import URLSafeSerializer from itsdangerous import URLSafeTimedSerializer class TestURLSafeSerializer(TestSerializer): @pytest.fixture() def serializer_factory(self): return partial(URLSafeSerializer, secret_key="secret-key") @pytest.fixture(params=({"id": 42}, pytest.param("a" * 1000, id="zlib"))) def value(self, request): return request.param class TestURLSafeTimedSerializer(TestURLSafeSerializer, TestTimedSerializer): @pytest.fixture() def serializer_factory(self): return partial(URLSafeTimedSerializer, secret_key="secret-key") itsdangerous-1.1.0/tox.ini0000644000175000017500000000160013364722330015733 0ustar daviddavid00000000000000[tox] envlist = py{37,36,35,34,27,py3,py} stylecheck docs-html coverage-report skip_missing_interpreters = true [testenv] setenv = COVERAGE_FILE = .coverage.{envname} deps = pytest-cov freezegun commands = pytest --tb=short --cov --cov-report= {posargs} [testenv:stylecheck] deps = pre-commit skip_install = True commands = pre-commit run --all-files --show-diff-on-failure [testenv:docs-html] deps = -r docs/requirements.txt commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html [testenv:coverage-report] setenv = COVERAGE_FILE = .coverage deps = coverage skip_install = true commands = coverage combine coverage html coverage report [testenv:codecov] passenv = CI TRAVIS TRAVIS_* setenv = COVERAGE_FILE = .coverage deps = codecov skip_install = true commands = coverage combine codecov coverage report