a fŸWc”¡ã@sRdZddlmZmZmZddlZe e¡Zddl Z ddl Z ddl m Z mZmZddlmZmZmZddlmZddlmZmZmZmZddlmZmZmZdd lm Z dd l!m"Z"m#Z#m$Z$dd l%m&Z&gd ¢Z'eekZ(e(r0dd l)m*Z*m+Z+e,e*e+ƒse-de*fƒ‚e*j.se* /¡ddl0m Z e  1ddg¡edkrFe 2de¡e3ƒZ4dd„Z5e(rvddl6m7Z7Gdd„de7ƒZ8dd„Z9dd„Z:d1dd„Z;edkr¦e;e j<ƒZ=n ed kr¼e;e j>ƒZ=n e;e jƒZ=dd!l?m@Z@eAd"e@jBe= Cd#¡d$ Dd"¡fd!ZEGd%d&„d&e3ƒZFGd'd(„d(e"eFƒZGd)ZHd*ZIGd+d,„d,eGƒZJGd-d.„d.eJƒZKGd/d0„d0eGƒZLdS)2ztest passlib.ext.djangoé)Úabsolute_importÚdivisionÚprint_functionN)ÚappsÚexcÚregistry)Údjango10_contextÚdjango14_contextÚdjango16_context)Ú CryptContext)ÚDJANGO_VERSIONÚMIN_DJANGO_VERSIONÚDjangoTranslatorÚquirks)Ú iteritemsÚget_method_functionÚu)Úmemoized_property)ÚTestCaseÚ TEST_MODEÚhandler_derived_from)Úget_handler_case)ÚDjangoBehaviorTestÚExtensionBehaviorTestÚDjangoExtensionTestÚ_ExtensionSupportÚ_ExtensionTest)ÚsettingsÚ LazySettingsz4expected django.conf.settings to be LazySettings: %r)rzdjango.contrib.contenttypeszdjango.contrib.auth)ééz1this release hasn't been tested against Django %rcKs@t|ƒD]2\}}|tur.tt|ƒr:tt|ƒqtt||ƒqdS)z*helper to update django settings from kwdsN)rÚUNSETÚhasattrrÚdelattrÚsetattr)ÚkwdsÚkÚv©r(úA/usr/lib/python3.9/site-packages/passlib/tests/test_ext_django.pyÚupdate_settingsNs   r*)ÚUserc@s<eZdZdZGdd„dƒZedd„ƒZdd„Zd d d „ZdS) ÚFakeUserz#mock user object for use in testingc@seZdZeZdS)z FakeUser.MetaN)Ú__name__Ú __module__Ú __qualname__Z app_labelr(r(r(r)ÚMeta_sr0cCsgS©Nr(©Úselfr(r(r)Úsaved_passwordsbszFakeUser.saved_passwordsc Cs0z|jdd…W|jdd…=S|jdd…=0dSr1)r4r2r(r(r)Úpop_saved_passwordsfs  þzFakeUser.pop_saved_passwordsNcCs|j |j¡dSr1)r4ÚappendÚpassword)r3Z update_fieldsr(r(r)Úsavelsz FakeUser.save)N) r-r.r/Ú__doc__r0rr4r5r8r(r(r(r)r,Zs  r,cs&g‰‡fdd„}‡fdd„}||_|S)Ncsˆ |¡dSr1)r6)r7©Ústater(r)Úsetterrsz"create_mock_setter..setterc s*zˆdd…Wˆdd…=Sˆdd…=0dSr1r(r(r:r(r)Úpopstatets   þz$create_mock_setter..popstate)r=)r<r=r(r:r)Úcreate_mock_setterps   r>c Csp|sJ‚ddlm}z|d|dWdStyj}z,t dt|ƒ¡rTWYd}~dS‚WYd}~n d}~00dS) z| check whether django hasher is available; or if it should be skipped because django lacks third-party library. r©Ú make_passwordÚ©ÚhasherTz3Couldn't load '.*?' algorithm .* No module named .*NF)Údjango.contrib.auth.hashersr@Ú ValueErrorÚreÚmatchÚstr)Únamer@Úerrr(r(r)Úcheck_django_hasher_has_backend}s  rKcCsRt|dƒr| ¡}|jdd|durGóz5_ExtensionSupport.unload_extension..N) rprqrdrsZ remove_patchr*Údictrƒrv)r3rur(r(r)r‚=s   z"_ExtensionSupport.unload_extension)N)T) r-r.r/r9Ú classmethodrlrvr}rƒrŠr‚r(r(r(r)rÏs $  rcs eZdZdZ‡fdd„Z‡ZS)rz€ TestCase mixin which makes sure extension is unloaded before test; and make sure it's unloaded after test as well. csNtt|ƒ ¡| d¡ts(| d¡‚nts6| d¡‚| ¡| |j¡dS)NÚdefaultzDjango not installedzDjango version too old) ÚsuperrÚsetUpZrequire_TEST_MODEr ÚskipTestrRr‚Z addCleanupr2©Ú __class__r(r)r’]s   z_ExtensionTest.setUp)r-r.r/r9r’Ú __classcell__r(r(r”r)rTsrZtoomanysecretsZletmeinc@sŽeZdZdZdZdZeZedd„ƒZ dd„Z e dfd d „Z d d „Z d d„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„Zdd„ZdS)ra  tests model to verify it matches django's behavior. running this class verifies the tests correctly assert what Django itself does. running the ExtensionBehaviorTest subclass below verifies "passlib.ext.django" matches what the tests assert. zverify django behaviorFcCs t |j¡S)z? per-test CryptContext() created from .config. )r ryr‡r2r(r(r)r|šszDjangoBehaviorTest.contextcCs4| |j d¡¡| | ¡¡| | ¡g¡dS)zO check that user object is set to 'unusable password' constant ú!N)rtr7rbrrÚhas_usable_passwordrzr5)r3Úuserr(r(r)Úassert_unusable_password¡sz+DjangoBehaviorTest.assert_unusable_passwordNcCsn|tur&| |jd¡| |jd¡n| |j|¡| | ¡d|jf¡| | ¡|durbgn|g¡dS)zÚ check that user object has a usable password hash. :param hash: optionally check it has this exact hash :param saved: check that mock commit history for user.password matches this list r—Nzhash should be usable: %r)r!ÚassertNotEqualr7rzrtr˜r5)r3r™ÚhashÚsavedr(r(r)Úassert_valid_password©s  ÿ ÿz(DjangoBehaviorTest.assert_valid_passwordcCsf|js| d¡‚|j}ddlm}ddlm}| |jdd|jdd¡ddl m}| ||¡dS)z; test extension config is loaded correctly zextension not loadedr)r\)r{TrwN) rhr“r|rDr\r…r{rzrLÚdjango.contrib.auth.models)r3Úctxr\r{Zcheck_password2r(r(r)Útest_extension_configÎs    z(DjangoBehaviorTest.test_extension_configcCsf|j}ddlm}tƒ}| t¡| | ¡ t|j ¡¡|  |¡|tƒ}| | ¡ t|¡¡dS)z1 test django's default algorithm rr?N) r|rDr@r,r]ÚPASS1rtÚhandlerÚverifyr7rž)r3r r@r™rœr(r(r)Útest_default_algorithmàs   z)DjangoBehaviorTest.test_default_algorithmcCs†|j}ddlm}m}m}m}tƒ}| d¡|j}|  |  ¡  d|¡¡|  ||¡|  | d¡¡|  ||¡|  |d|ƒ¡dS)zB test how methods handle empty string as password r©r\r@Úis_password_usabler^rAN) r|rDr\r@r§r^r,r]r7rtr£r¤rž)r3r r\r@r§r^r™rœr(r(r)Útest_empty_password÷s   z&DjangoBehaviorTest.test_empty_passwordcCsìddlm}m}m}m}tƒ}| ¡| |¡tƒ}| d¡| |¡|  | d¡¡|  | d¡¡|  | d¡¡|  | t ¡¡|  | t ¡¡| |¡|  |dƒ  d¡¡|  |t dƒ¡|  ||jƒ¡| t||j¡dS)zA test how methods handle 'unusable flag' in hash rr¦NÚNonerAr—)rDr\r@r§r^r,Zset_unusable_passwordršr]rrr¢ÚWRONG1rtrbr7Ú assertRaisesrE©r3r\r@r§r^r™r(r(r)Útest_unusable_flags"    z%DjangoBehaviorTest.test_unusable_flagcCs¦|j}ddlm}m}m}m}tƒ}d|_tj rF|sF|  t |jt ¡n|  | t ¡¡| | ¡tj¡tj r„|s„|  t |t d¡n|  |t dƒ¡|  t |d¡dS)z< test how methods handle None as hash value rr¦N)rhrDr\r@r§r^r,r7rZ none_causes_check_password_errorr«Ú TypeErrorr¢rrrzr˜Úempty_is_usable_password)r3rhr\r@r§r^r™r(r(r)Útest_none_hash_valueAs   ÿ z'DjangoBehaviorTest.test_none_hash_valuecCs†ddlm}m}m}m}tƒ}d|_| | t¡¡|  |jd¡|  |  ¡g¡|  |  ¡t j ¡| |tdƒ¡| t|d¡dS)zD test how methods handle empty string as hash value rr¦rAN)rDr\r@r§r^r,r7rrr¢rzr5r˜rr¯r«rEr¬r(r(r)Útest_empty_hash_valuejs z(DjangoBehaviorTest.test_empty_hash_valuec CsDdD]:}|j|d| |¡Wdƒq1s40YqdS)z> test how methods handle invalid hash values. )z$789$foo)rœN)ÚsubTestÚ_do_test_invalid_hash_value)r3rœr(r(r)Útest_invalid_hash_valuessz+DjangoBehaviorTest.test_invalid_hash_valuescCs†ddlm}m}m}m}tƒ}||_| | t¡¡|  |j|¡|  |  ¡g¡|  |  ¡t j ¡| |t|ƒ¡| t||¡dS)Nrr¦)rDr\r@r§r^r,r7rrr¢rzr5r˜rZinvalid_is_usable_passwordr«rE)r3rœr\r@r§r^r™r(r(r)r³™s z.DjangoBehaviorTest._do_test_invalid_hash_valuec CsJ|j ¡D]:}|j|d| |¡Wdƒq 1s:0Yq dS)z run a bunch of subtests for each hasher available in the default django setup (as determined by reading self.context) )ÚschemeN)r|Úschemesr²Ú_do_test_available_scheme)r3rµr(r(r)Útest_available_schemes¼sz)DjangoBehaviorTest.test_available_schemescCs | ¡}|j}|j}tƒ}ddlm}m}m}m} |  |¡} |  d|| ¡|  |¡j } | rj||  ¡ksjJ‚z t |ƒ} Wntjy”| d¡‚Yn0t| | j ƒs¦J‚| jr¶| d¡‚|sît| jƒsî|dvsØJd|ƒ‚| d|¡| d ¡‚zt|\} }Wn6ty4| d ƒj}|ƒ\} }| rq0qYn0d }tƒ}||_| | d ¡¡| | |¡¡| ||¡| | | ¡¡| }|rÖ| |j|¡| |  |j¡¡| |  ¡  | |j¡¡|j||jd n | ||¡t!ddròd St"ƒ #|¡}|| |d}| |   | |¡¡| || ||d¡| $| %¡|rF| gng¡| ||||d¡| $| %¡g¡| ||ƒ¡t"ƒ &| |ƒj'¡}| $||¡d S)z‹ helper to test how specific hasher behaves. :param scheme: *passlib* name of hasher (e.g. "django_pbkdf2_sha256") rr¦ztesting scheme: %r => %rzbackend not availablezskip disabled hasher)Ú django_bcryptZdjango_bcrypt_sha256Z django_argon2z+%r scheme should always have active backendz3skipping scheme %r due to missing django dependencyzskip due to missing dependencyr’Z dontletmeinN)rr)ÚmaxrB)r<)(Ú getLoggerr|rhr>rDr\r@r§r^r£ÚdebugrNZdefault_schemerrZMissingBackendErrorr“rZ is_disabledrKZ django_nameZwarningÚ sample_hashesÚKeyErrorÚget_sample_hashr,r7rrržrtr›Zidentifyr¤rrZpasslib_to_django_namerzr=Zdjango_to_passlib_nameÚ algorithm)r3rµÚlogr rhr<r\r@r§r^r£rNZtestcaseZsecretrœr¿Úotherr™Z needs_updateZalgZhash2rIr(r(r)r·Åsn     ÿ           z,DjangoBehaviorTest._do_test_available_scheme)r-r.r/r9ÚdescriptionPrefixrhÚ stock_configr‡rr|ršr!ržr¡r¥r¨r­r°r±r´r³r¸r·r(r(r(r)rzs$   %"()% # rcs0eZdZdZdZedddZ‡fdd„Z‡ZS)rz` test that "passlib.ext.django" conforms to behavioral assertions in DjangoBehaviorTest zverify extension behaviorz sha256_crypt,md5_crypt,des_cryptZ des_crypt)r¶rNcs&tt|ƒ ¡|j|jdd|_dS)N©r~T)r‘rr’rŠr‡rhr2r”r(r)r’NszExtensionBehaviorTest.setUp) r-r.r/r9rÃrŽr‡r’r–r(r(r”r)rCsþrc@sTeZdZdZdZdd„Zdd„Zdd„Zd d „Zd d „Z d d„Z dd„Z dd„Z dS)rz0 test the ``passlib.ext.django`` plugin zpasslib.ext.django plugincCsž|jddd| ¡| d¡|jdddWdƒn1sD0Y| ¡|jddd|jtd| ¡|jddd|jtd| ¡dS) z.test set_django_password_context patch/unpatchÚdisabledF©r~r†ú!PASSLIB_CONFIG=None is deprecatedNú django-1.0rú django-1.4)rŠrvÚassertWarningListr}rr‚r r2r(r(r)Útest_00_patch_controlgs ,  z)DjangoExtensionTest.test_00_patch_controlcCsÒd}|j|dddlmmm}ddlm}dd„}|jj}||j_|  d¡|j   ¡Wdƒn1sv0Y||j_|j }||_ |  d ¡|j   ¡Wdƒn1s¾0Y||_ dS) z(test detection of foreign monkeypatchingz[passlib] schemes=des_crypt rÅrN)rscSsdSr1r(r(r(r(r)ÚdummyŽsz>DjangoExtensionTest.test_01_overwrite_detection..dummyz/another library has patched.*User\.set_passwordz2another library has patched.*models:check_password) rŠrŸZcontribZauthrZr…rsr+r]rËZ_managerZ check_allr\)r3r‡rZrsrÍZorigr(r(r)Útest_01_overwrite_detections   ( (z/DjangoExtensionTest.test_01_overwrite_detectioncCs ddlm}tƒj}tdkr,| t|d¡n|dƒ}| ||j¡|dƒ}| ||j ¡ddl m }|dƒ}|  |j d¡| d ¡}| | d |¡¡| | d |¡¡| | d |¡¡|jd d d d }|  |d¡|  | |¡dtdƒd tdƒdœ¡| t|d¡dS)z'test Hasher-compatible handler wrappersr)r[rUZhex_md5r¹©Ú sha256_cryptrÐZpasslib_sha256_cryptÚstubZxxxxZabcdabcdabcdabcdiÒrXzK$5$rounds=1234$abcdabcdabcdabcd$v2RWkZQzctPdejyRqmmTDQpZN6wTh7.RUy9zF2LftT6zabcdab**********z+v2RWkZ*************************************)rÀZsaltrYrœZdoes_not_existN)r`r[rÚpasslib_to_djangor r«rEZassertIsInstanceZUnsaltedMD5PasswordHasherZBCryptPasswordHasherÚ passlib.hashrÐrzrÀÚencodertr¤rrZ safe_summaryrr¾)r3r[rÒrCrÐZencodedr(r(r)Útest_02_handler_wrapperŸs2     ýÿ z+DjangoExtensionTest.test_02_handler_wrappercCsZ| d¡|jdddWdƒn1s.0Y| ¡|jddd| ¡dS)ztest PASSLIB_CONFIG='disabled'rÈNFrÇrÆ)rËrŠrvr2r(r(r)Útest_11_config_disabledÏs  ,z+DjangoExtensionTest.test_11_config_disabledcCsP|jdddt}| |¡|jddd| t¡|jddd| t¡dS)ztest PASSLIB_CONFIG=''zdjango-defaultF©rr†rÉrÇrÊN)rŠr r}rr )r3r r(r(r)Útest_12_config_presetsÚs  z*DjangoExtensionTest.test_12_config_presetscCs\ddlm}t |¡}| ¡| |¡|jddd| |¡|j|dd| |¡dS)z$test PASSLIB_CONFIG default behaviorr)ÚPASSLIB_DEFAULTzpasslib-defaultFr×N)Úpasslib.ext.django.utilsrÙr Ú from_stringrŠr})r3rÙrr(r(r)Útest_13_config_defaultsçs    z+DjangoExtensionTest.test_13_config_defaultscCs@tdtd| ttd¡| ¡tdtd| ttd¡dS)ztest PASSLIB_CONFIG type checksé{)rr~rmzmissing-preset)r~rN)r*r!r«r®Ú __import__r‚rEr2r(r(r)Útest_14_config_invalid÷s   z*DjangoExtensionTest.test_14_config_invalidcs8tdgdddd}ddlm‰‡fdd „}|j|d | |ƒd¡| |d d d¡| |d d d¡dd„}|j||d| |ƒd¡| |ddd¡| |ddd¡| |ddd¡dd„}|j||d| |ƒd¡| |ddd¡| |dd dd¡| |dd dd¡|jt|j|dddS)z#test PASSLIB_GET_CATEGORY parameterrÐièiÐi¸ )r¶Zsha256_crypt__default_roundsZ#staff__sha256_crypt__default_roundsZ'superuser__sha256_crypt__default_roundsrrÏcs&tfi|¤Ž}| d¡ˆ |j¡jS)z;helper to take in user opts, return rounds used in passwordrÑ)r,r]rÛr7rY)r%r™rÏr(r)Úruns z9DjangoExtensionTest.test_21_category_setting..runrÅT)Úis_staff)Ú is_superusercSs |jpdSr1©Ú first_name©r™r(r(r)Ú get_categoryszBDjangoExtensionTest.test_21_category_setting..get_category)rr€rÂrãZstaffZ superusercSsdSr1r(rår(r(r)ræ%s)rärá)rärâÚxN)rŽrÓrÐrŠrzr«r®)r3r‡ràrær(rÏr)Útest_21_category_settings>ü   ÿÿ ÿz,DjangoExtensionTest.test_21_category_settingN) r-r.r/r9rÃrÌrÎrÕrÖrØrÜrßrèr(r(r(r)rYs0   r)N)Mr9Z __future__rrrZloggingr»r-rÁrprFr‰rZ_appsrrZ passlib.appsrr r Zpasslib.contextr rÚr r rrZpasslib.utils.compatrrrZpasslib.utils.decorrZpasslib.tests.utilsrrrZpasslib.tests.test_handlersrÚ__all__rRZ django.confrrÚ isinstanceÚ RuntimeErrorZ configuredZ configureZ django.appsZpopulateÚinfoÚobjectr!r*rŸr+r,r>rKrSZdjango21_contextrÄZdjango110_contextrÓrWrŽZusingrdrœr½rrr¢rªrrrr(r(r(r)Úsp            )      ÿþÿ "L