a fWcU@@sdZddlmZddlmZddlZddlmZmZddl Z ddl Z e e Z ddlZddlZddlZddlZddlZddlZddlZddlmZmZddlmZmZddlZddlmZdd lmZdd lmZddl m!Z!dd l"m#Z$m%Z%m&Z&m'Z'm(Z(dd l)m*Z*m+Z+m,Z,m-Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4dd lm5Z5m6Z6m7Z7m8Z8m9Z9m:Z:ddl;mm?Z@gdZAz ddlBZCWneDydZEYn0dZEddZFddZGeGe3ZHgdZIeIJejKLddMNZOdNddZPddZQdOddZRdd ZSdPd!d"ZTd#d$ZUd%d&ZVd'd(ZWejXd)d*ZYd+d,ZZd-d.Z[d/d0Z\d1d2Z]d3d4Z^d5d6Z_dQd9d:Z`dRd=d>ZaGd?d@d@e$Z#dAdgZbdBdCZcGdDdEdEe#ZdGdFdGdGedZeGdHdIdIedZfGdJdKdKedZgGdLdMdMejhZidS)Szhelpers for passlib unittests)with_statement) unhexlifyN)wrapspartial)PasslibHashWarningPasslibConfigWarning)PY3JYTHON)warn)exc)MissingBackendError)TestCaseskipskipIf skipUnlessSkipTest) has_rounds_info has_salt_inforounds_cost_valuesrng getrandstr is_ascii_safe to_native_str repeat_stringtickbatch) iteritemsirangeuunicodePY2 nullcontext) classproperty) TEST_MODEset_fileget_filer HandlerCaseFTcCs8tj|}tj||kr4tdt|dq dS)zensure file's mtime has changed皙?N)ospathgetmtimetimesleeputime)r)Zlastr.7/usr/lib/python3.9/site-packages/passlib/tests/utils.pyensure_mtime_changed;s  r0cs&fddtfddtdDS)Ncs"}}||kr}q ||SNr.)startZcurtimerr.r/sampleEs z%_get_timer_resolution..samplec3s|] }VqdSr1r..0_)r5r.r/ Jz(_get_timer_resolution..)minranger3r.)r5r4r/_get_timer_resolutionDs r>)quickdefaultfullZPASSLIB_TEST_MODEr@cCs0|rtt|krdS|r,tt|kr,dSdS)acheck if test for specified mode should be enabled. ``"quick"`` run the bare minimum tests to ensure functionality. variable-cost hashes are tested at their lowest setting. hash algorithms are only tested against the backend that will be used on the current host. no fuzz testing is done. ``"default"`` same as ``"quick"``, except: hash algorithms are tested at default levels, and a brief round of fuzz testing is done for each hash. ``"full"`` extra regression and internal tests are enabled, hash algorithms are tested against all available backends, unavailable ones are mocked whre possible, additional time is devoted to fuzz testing. FT) _test_mode _TEST_MODESindex)r<maxr.r.r/r#Ts r#cCs$t|drdSd|jvp"t|tjS)z'check if handler supports 'relaxed' kwd orig_prefixFrelaxed)hasattr setting_kwds issubclassuhZGenericHandlerhandlerr.r.r/has_relaxed_settingps  rNcCst|}||ddjS)z'get effective rounds value from handlerT)rounds use_defaults)unwrap_handlerrO)rMrOr.r.r/get_effective_rounds|srRc CsVz |}Wnty YdS0z"|d||kW||S||0dS)z*check if backend is the default for sourceFr@N) get_backendr set_backend)rMbackendorigr.r.r/is_default_backends     rWccsZ|dur|}|j}|r(||dnd}||dD]}||kr8||r8|Vq8dS)z iterate over alternate backends available to handler. .. warning:: not thread-safe due to has_backend() call Nr)rSbackendsrD has_backend)rMcurrentfallbackrYidxrUr.r.r/iter_alt_backendssr^cOst|i|D] }|SdSr1)r^)argskwdsrUr.r.r/get_alt_backendsracCst|dr|j}q|S)z5return original handler, removing any wrapper objectswrapped)rHrbrLr.r.r/rQs rQcCsf||kr dSt|tjr4|r0||kr(dS|j}qdSt|trTt|tjrTt||Std|fdS)zG test if was derived from via . TFz%don't know how to inspect handler: %rN) isinstancerK PrefixWrapperZ _derived_fromtyperJZMinimalHandlerNotImplementedError)rMbaser.r.r/handler_derived_froms  rhc#st|trJt|tjrJ|jfdd}||_zdVW|_q|_0nHt|tjrt|jdVWdq1s|0Yn dVdSdS)a internal helper for do_config_encrypt() -- context manager which temporarily replaces handler's _calc_checksum() with one that uses min_rounds; useful when trying to generate config with high rounds value, but don't care if output is correct. cs:|j}z&|j|_|g|Ri|W||_S||_0dSr1)rO min_rounds)selfr_r`rOrbr.r/wrappersz&patch_calc_min_rounds..wrapperN) rcrerJrKZ HasRoundsZ_calc_checksumrdpatch_calc_min_roundsrb)rMrlr.rkr/rms   &rmcCsLt|tr|d}t|d}||Wdn1s>0YdS)zset file to specified bytesutf-8wbN)rcrencodeopenwrite)r)Zcontentfhr.r.r/r$s   r$cCs6t|d}|WdS1s(0YdS)zread file as bytesrbN)rqread)r)rsr.r.r/r%s r%cCsJt|ts|Str|dSz |dWStyD|dYS0dS)z*convert native string to non-native stringrnlatin-1N)rcstrrrpdecodeUnicodeDecodeErrorsourcer.r.r/tonns    r|cCsttdd|S)zT helper for represent byte strings in hex. usage: ``hb("deadbeef23")`` z\s)rresubrzr.r.r/hbsrcCs||kr |S||kr|S|Sr1r.)valuelowerupperr.r.r/limits rcCst}t||krqdS)zAbecause time.sleep() doesn't even have 10ms accuracy on some OSesNr)Zdelayr2r.r.r/ quicksleepsrrX c Cstddlm}ddlm}|||p"dd}d}t|}t|||} t|krf| |t||dfS|d9}q6dS) z timeit() wrapper which tries to get as accurate a measurement as possible w/in maxtime seconds. :returns: ``(avg_seconds_per_call, log10_number_of_repetitions)`` r)Timerlogr})setuprXrN)Ztimeitrmathrrr<repeatint) funcrZmaxtimeZbestofrrr4ZnumberendZdeltar.r.r/ time_call s    rF "*!csfdd}|S)z< decorator run test method w/ multiple fixed seeds. cstfdd}|S)Ncs8t}tD] }|d|d<|i|qdS)N seed)randomRandomr getrandbits)r_r`rr8)countr master_seedr.r/rl s  z6run_with_fixed_seeds..builder..wrapper)r)rrlrrrr/buildersz%run_with_fixed_seeds..builderr.)rrrr.rr/run_with_fixed_seedssrcs.eZdZdZdZfddZeddZeddZd Z d Z fd d Z d d Z d Z ddZd3fdd ZddZeZZd4ddZGdddejZd5ddZd6ddZddZddZd d!Zd"d#Zd$d%ZeZ dZ!dZ"d7d'd(Z#e$e%d)Z&e'j(fd*d+Z)dZ*d,d-Z+d8d/d0Z,d1d2Z-Z.S)9r aRpasslib-specific test case class this class adds a number of features to the standard TestCase... * common prefix for all test descriptions * resets warnings filter & registry for every test * tweaks to message formatting * __msg__ kwd added to assertRaises() * suite of methods for matching against warnings Ncs0tt|}|j}|r,d||p&t|f}|S)z;wrap shortDescription() method to prepend descriptionPrefixz%s: %s)superr shortDescriptiondescriptionPrefixrw)rjdescprefix __class__r.r/rBs zTestCase.shortDescriptioncCs |j}|dpt|d|dS)Nr8z_%s__unittest_skipF)__name__ startswithgetattr)clsnamer.r.r/__unittest_skip__Ns zTestCase.__unittest_skip__cCs|j Sr1)rrr.r.r/__test__UszTestCase.__test__Tcs(tt|||tdddS)NZENABLE_DEBUG_ONLY_REPRT)rr setUp setUpWarnings patchAttrr rjrr.r/rdszTestCase.setUpcCsH|jrDt}|||jtddtddtdddS)z6helper to init warning filters before subclass setUp()ignorez) rHrrlinerrerrrw)rjrtailrr.r.r/_formatWarnings  zTestCase._formatWarningcsddfdd|DS)Nz[%s], c3s|]}|VqdSr1)r)r7rrr.r/r9r:z.TestCase._formatWarningList..)join)rjrr.rr/rszTestCase._formatWarningListcCs.ddlm}|s*ddlm}|d|dS)z,helper to skip test if stringprep is missingr) stringprep)_stringprep_missing_reasonz%not available - stringprep module is N) passlib.utilsrrskipTest)rjrrr.r.r/require_stringpreps   zTestCase.require_stringprepcCst|s|d|dS)z8skip test for all PASSLIB_TEST_MODE values below zrequires >= %r test modeN)r#r)rjlevelr.r.r/require_TEST_MODE!szTestCase.require_TEST_MODEcCstr|dSdS)z'skip test if writeable FS not availablez.GAE doesn't offer read/write filesystem accessN)GAErrr.r.r/require_writeable_filesystem&sz%TestCase.require_writeable_filesystemr@c Cs*|j}|r||vr||S|j|j}|rL||vrL||WdS|sZi}|_|pbtj}|durttjdptjdpt d}t_t d|t |}d t||j|j|j|g}t|d}t|ddd}t|}||<|WdS1s0YdS) aU Return a :class:`random.Random` object for current test method to use. Within an instance, multiple calls with the same name will return the same object. When first created, each RNG will be seeded with value derived from a global seed, the test class module & name, the current test method name, and the **name** parameter. The global seed taken from the $RANDOM_TEST_SEED env var, the $PYTHONHASHSEED env var, or a randomly generated the first time this method is called. In all cases, the value is logged for reproducibility. :param name: name to uniquely identify separate RNGs w/in a test (e.g. for threaded tests). :param seed: override global seed when initialzing rng. :rtype: random.Random NZRANDOM_TEST_SEEDZPYTHONHASHSEEDrzusing RANDOM_TEST_SEED=%d rn) _random_cache_random_global_lockr _random_global_seedrr(environgetsys_rngrrinforerrwrr_testMethodNamehashlibZsha256rp hexdigestrr) rjrrcacheZ global_seedrr{digestrr.r.r/ getRandom9s4       zTestCase.getRandomsubTestc /sddd}|}||i|}|jr>tt|j|i|}nt}||d|z dVWn\ty~|d|Yn@ty}z(| d|t |j t |WYd}~n d}~00Wdn1s0Y|d|dS) zt wrapper/backport for .subTest() which also traps SkipTest errors. (see source for details) Nc[s@|r d|nd}|r4|dddd|D7}|p>dS)Nz[%s] r}z(%s) css|]}dt|VqdS)z%s=%rN)r)r7itemr.r.r/r9r:z:TestCase.subTest.._render_title..z )ritemsstrip)Z_msgparamsoutr.r.r/ _render_titles z'TestCase.subTest.._render_titlezrunning subtest: %szsubtest skipped: %szsubtest failed: %s: %s: %rzsubtest passed: %s)N) getLoggerhas_real_subtestrr rr!r r Exceptionrrerrw)rjr_r`rZtest_logtitlerrrr.r/r|s"     6zTestCase.subTestcsb|tj|i|\}}t||jdurTg|_fdd}||||S)z1create temp file that's cleaned up at end of testNcs.D]}tj|rt|qdd=dSr1)r(r)existsremove)r)Zqueuer.r/cleaners  z TestCase.mktemp..cleaner)rtempfileZmkstempr(close _mktemp_queuerappend)rjr_r`fdr)r r.rr/mktemps     zTestCase.mktempFcszzt}Wn0ty>|r"fdd}||Yn0|t||rjt||}t||t|dS)z=monkeypatch object value, restoring original value on cleanupcs&ztWnty Yn0dSr1)delattrAttributeErrorr.attrobjr.r/cleanups z#TestCase.patchAttr..cleanupN)rr(rsetattrrr)rjr+r*rZrequire_existingwraprVr,r.r)r/rs   zTestCase.patchAttrcCs@t|}|jdt|d|j}|j}|r6|d|}t|S)z9 return logger named after current test. .r)rerrrr loggingr)rjrr)rr.r.r/rs  zTestCase.getLogger)N)NNNNNNN)NNN)N)r@N)TF)/rrrrrrr"rrZ_TestCase__unittest_skiprrrrrrrZassertNotEqualsZassertRegexMatchesrrcatch_warningsrrrrrrrr threadingLockrrrrrH _TestCaser contextlibcontextmanagerrr#r&rrrr.r.rr/r -sR      0     A 8 r anycCs d|_|S)a0 decorator for HandlerCase.create_backend_case() -- used to decorate methods that should be run even if backend isn't present (by default, full test suite is skipped when backend is missing) NOTE: tests decorated with this should not rely on handler have expected (or any!) backend. T)_doesnt_require_backendrr.r.r/doesnt_require_backendsr9cs<eZdZdZdZdZgZgZgZgZ gZ gdZ e de ddgZ dZdZdZdZedd Zd Zed d Zed dZddZdddZddZddZddZdddZdddZddZ dd Z!d!d"Z"dd#d$Z#d%Z$ed&d'Z%ed(d)Z&dZ'd*d+Z(fd,d-Z)d.d/Z*d0d1Z+d2d3Z,dd4d5Z-d6d7Z.d8d9Z/d:d;Z0dd?Z2d@dAZ3edBdCZ4dDdEZ5dFdGZ6dHdIZ7dZ8dJdKZ9dLdMZ:edNdOZ;dPdQZdVdWZ?dXdYZ@dZd[ZAfd\d]ZBd^d_ZCd`daZDdbdcZEdddeZFdfdgZGdhdiZHdjdkZIdldmZJdndoZKdpdqZLdrdsZMdtduZNdvdwZOdxdyZPdzd{ZQd|d}ZRd~dZSddZTddZUddZVddZWddZXddZYddZZddZ[ddZ\ddZ]gZ^ddZ_ddZ`ddZaddZbdddZcddZdeddZeeddZfdZgdddZhddZiGdddejZkddZlZmS)r&abase class for testing password hash handlers (esp passlib.utils.handlers subclasses) In order to use this to test a handler, create a subclass will all the appropriate attributes filled as listed in the example below, and run the subclass via unittest. .. todo:: Document all of the options HandlerCase offers. .. note:: This is subclass of :class:`unittest.TestCase` (or :class:`unittest2.TestCase` if available). N))Z des_cryptZ 6f8c114b58f2c)Z md5_cryptz"$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o.)Z sha512_cryptzx$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1testu€¥$s€¥$FcCsdt|jddvrdSdS)Nos_cryptrYr.)rrMrr.r.r/forbidden_charactersZsz HandlerCase.forbidden_charactersTcCs,|j}|j}t|dr(|d|f7}|S)NrSz (%s backend))rMrrHrS)rjrMrr.r.r/rgs  zHandlerCase.descriptionPrefixccsV|jD]\}}||fVq|jD]\}}}||fVq |jD]\}}}||fVqdS|sNd||f}||n"|rbdS|srd||f}||dS)z>helper to check verify() outcome, honoring is_disabled_handlerTFz'verify() returned non-boolean value: %rNz4verify incorrectly returned True: secret=%r, hash=%rz!verify failed: secret=%r, hash=%r) do_verify assertTruerM is_disabledr)rjrArBrnegaterr.r.r/ check_verifys"    zHandlerCase.check_verifycCs||td||fdS)Nz'%s() failed to return native string: %r)rrw)rjrZ func_namer.r.r/check_returned_native_strs z%HandlerCase.check_returned_native_strcCs||j}d|jvrxd|vrx|j}|j}tddr>td||d<n:d}t|dddkr\||8}n |d|>}td|||d<dS) z0subclassable method to populate default settingsrOr?rEr; rounds_costNlog2rX)rMrIridefault_roundsr#rEr)rjr`rMmndfZfactorr.r.r/populate_settingss   zHandlerCase.populate_settingscCs|S)z?subclassable method allowing 'secret' to be encode context kwdsr.)rjrAr`r.r.r/populate_contextszHandlerCase.populate_contextcKs|||duri}|||}|rg}|rH|jfi||d||(|p\|jj|fi|WdS1s0Yn$|p|jjfi|j|fi|SdS)z3call handler's hash() method with specified optionsNz"passing settings to.*is deprecated) rTrUupdater$rrMZencryptusingrB)rjrA use_encryptrMcontextsettingsrr.r.r/ do_encrypts    8zHandlerCase.do_encryptcKs&|||}|p|jj||fi|S)zcall handler's verify method)rUrMverify)rjrArBrMr`r.r.r/rHs zHandlerCase.do_verifycCs |j|S)zcall handler's identify method)rMidentifyrjrBr.r.r/ do_identifyszHandlerCase.do_identifycKs|||jjfi|S)z6call handler's genconfig method with specified options)rTrM genconfig)rjr`r.r.r/ do_genconfigs zHandlerCase.do_genconfigcKs"|||}|jj||fi|S)z4call handler's genhash method with specified options)rUrMgenhash)rjrArCr`r.r.r/ do_genhashs zHandlerCase.do_genhashcKsl|p|jjfi|}|dur"i}|d|}t|"|j|fi|WdS1s^0YdS)z return sample hash for handler, w/o caring if digest is valid (uses some monkeypatching to minimize digest calculation cost) Nr})rMrWrUrmrB)rjrMrYrZrAr.r.r/do_stub_encrypts   zHandlerCase.do_stub_encryptzbackend not availablecCs0|j}t||stdsdS||r*dS|jS)z helper for create_backend_case() -- returns reason to skip backend, or None if backend should be tested rAz$only default backend is being testedN)rMrWr#rZ_BACKEND_NOT_AVAILABLE)rrUrMr.r.r/_get_skip_backend_reasons  z$HandlerCase._get_skip_backend_reasonc Cs|j}|j}t|dsJd||jvs6Jd|f|f}|dkrN|tf7}td||f|td||f||||jd}|S)NrYz0handler must support uh.HasManyBackends protocolzunknown backend: %rr;z %s_%s_testz%s (%s backend))rrU_skip_backend_reasonr) rMrrHrY OsCryptMixinrerrfr)rrUrMrbasessubclsr.r.r/create_backend_cases$    zHandlerCase.create_backend_casecCst||jd}t|dd S)z] check if current test method decorated with doesnt_require_backend() helper Nr8F)rr )rjmethr.r.r/_test_requires_backendsz"HandlerCase._test_requires_backendcs|}|r|jr||jtt||j}|j}|rt|dsNt dz | |j | | |Wnt jjy|rYn0ddlm}||d|ddS)NrTz)handler doesn't support multiple backendsr)handlersrzsalt generator)rmrgrrr&rrMrUrH RuntimeErrorrrTrSrKr r rrnrr)rjZtest_requires_backendrMrUrnrr.r/r$s"    zHandlerCase.setUpcs|jfdd}|d}||d||td|||kd|td|d|f|d }||d ud ||td |d }||d ud||tdd S)zvalidate required attributescs t|dSr1)rrrLr.r/gaHsz3HandlerCase.test_01_required_attributes..garzname not defined:zname must be native strzname not lower-case:z ^[a-z0-9_]+$z&name must be alphanum + underscore: %rrINzsetting_kwds must be defined:zsetting_kwds must be a tuple:Z context_kwdszcontext_kwds must be defined:zcontext_kwds must be a tuple:)rMrIrrwrr~matchr)rjrqrrZrYr.rLr/test_01_required_attributesEs  z'HandlerCase.test_01_required_attributescCsT|}||d|d|}||d|d||||d|fdS)ztest basic config-string workflow this tests that genconfig() returns the expected types, and that identify() and genhash() handle the result correctly. r`stubrbr}z4identify() failed to identify genconfig() output: %rN)rarMrcrHrIr_)rjrCrr.r.r/test_02_config_workflowes     z#HandlerCase.test_02_config_workflowcCs.|j}|}|||||j|jdS)ztest basic using() workflowN)rMrWZ assertIsNotrr)rjrMrjr.r.r/test_02_using_workflows z"HandlerCase.test_02_using_workflowc Csd}|jD]}|j||d}||d||||j||dd|||}||d|jjr|jr|||d|||fn| ||d|||f|||}||d|jjr|js| ||d ||||fn|||d ||||f| | |q d S) ztest basic hash-string workflow. this tests that hash()'s hashes are accepted by verify() and identify(), and regenerated correctly by genhash(). the test is run against a couple of different stock passwords. rt)rXrBT)rKrbzBgenhash() failed to salt result hash: secret=%r hash=%r: result=%rz@genhash() failed to reproduce hash: secret=%r hash=%r: result=%rzYgenhash() failed to reproduce disabled-hash: secret=%r hash=%r other_secret=%r: result=%rzGgenhash() duplicated hash: secret=%r hash=%r wrong_secret=%r: result=%rN) stock_passwordsr[rMrLrcrMrJdisabled_contains_saltassertNotEqualrrIr_)rj use_16_legacyZ wrong_secretrArotherr.r.r/test_03_hash_workflows2             z!HandlerCase.test_03_hash_workflowcCs|jdddS)zEtest hash-string workflow with legacy .encrypt() & .genhash() methodsT)rzN)r|rr.r.r/test_03_legacy_hash_workflowsz(HandlerCase.test_03_legacy_hash_workflowcCs|td}||d|dt||tdt||dt|}||d|jjrv|jrv|||n | |||tdt|}||d|jjr|jr|||n | ||| | t|dS)z#test hashes can be unicode or bytesrtrBrbN) r[r|rMrLrcrMrJrxryrrIr_)rjrr{r.r.r/test_04_hash_typess     zHandlerCase.test_04_hash_typescCs|j}t|ds|d||j||jD]|}||t| |t d|f| |}|dur||| ||q2|dur| t|j|q2td||fq2dS)ztest multi-backend supportrTzhandler only has one backendzinvalid backend name: %rTFz*has_backend(%r) returned invalid value: %rN)rMrHrrrTrSrYrrw assertNotInRESERVED_BACKEND_NAMESrZrrr r)rjrMrUretr.r.r/test_05_backendss(       zHandlerCase.test_05_backendscCsd|jjvr|ddS)Nsaltzhandler doesn't have salt)rMrIrrr.r.r/ require_salt!s zHandlerCase.require_saltcCs |t|js|ddS)Nz!handler doesn't provide salt info)rrrMrrr.r.r/require_salt_info%s zHandlerCase.require_salt_infocCs||j}|j}|jdu}|r4|jdkr4|d|jdkrF|d|r^|j|jkr^|d|j|jkrr|d|r|j|jkr|dd |jvr|r|j|jkrtd |jf|j r|j s|d |j D]}||j vr|d |fqn|j s|d dS)z!validate optional salt attributesNrXzmax_salt_chars must be >= 1rzmin_salt_chars must be >= 0z(min_salt_chars must be <= max_salt_charsz*default_salt_size must be >= min_salt_sizez*default_salt_size must be <= max_salt_size salt_sizezT%s: hash handler supports range of salt sizes, but doesn't offer 'salt_size' settingz$default_salt_chars must not be emptyzEdefault_salt_chars must be subset of salt_chars: %r not in salt_charsz;default_salt_chars MUST be specified if salt_chars is empty) rrrM max_salt_size min_salt_sizedefault_salt_sizerIr r salt_charsdefault_salt_chars)rjrrZmx_setcr.r.r/ test_10_optional_salt_attributes*s4     z,HandlerCase.test_10_optional_salt_attributescCsB|j}t|sJd|jddlm}t|j|t|jdS)z%calculate number of salt bits in hashzneed explicit bit-size for rr) rMrrrrrrrr)rjrMrr.r.r/ salt_bitsSs  zHandlerCase.salt_bitscsDtddjfdd}|j|fdddS)z4test hash() / genconfig() creates new salt each timerXrcs<|}tD]}|}||krdSqdfdS)Nz.failed to find different salt after %d samples)rr)rZvalue1r8Zvalue2samplesrjr.r/sampleris z0HandlerCase.test_11_unique_salt..samplercs dSNrt)r[r.rr.r/rr:z1HandlerCase.test_11_unique_salt..N)rrErra)rjrr.rr/test_11_unique_salt_s  zHandlerCase.test_11_unique_saltcCs||j}|jdd}|j}||}|j|d|jd|d|dkrf|jt|j|ddd|jt|jd|dddS)z.test hash() / genconfig() honors min_salt_sizerrXrrtrNr)rrMrrrar[r ValueError)rjrM salt_charZmin_sizes1r.r.r/test_12_min_salt_sizets    z!HandlerCase.test_12_min_salt_sizec Cs@||j}|j}|jdd}|dus2|dkrp|d}|j|d}|j||d}||||jddn||}|j|d}|j|d||}|jt|j|d|jt|j|ddt|rt j dd |j|dd }Wdn1s0Y| |||j |kr<|j|dd d}|||dS) z.test hash() / genconfig() honors max_salt_sizerrXNiirrTr)rrGr) rrMrrrdryrrrNrr1rr) rjrMmax_sizerrZc1Zc2s2Zc3r.r.r/test_13_max_salt_sizes.     ,  z!HandlerCase.test_13_max_salt_sizecCs |jrddlm}||}|S)zprepare generated saltr)bcrypt64)fuzz_salts_need_bcrypt_repairZpasslib.utils.binaryrZ repair_unused)rjrrr.r.r/ prepare_salts  zHandlerCase.prepare_saltc Cs||j}|j}|j}|j}t|t}t||p4dD]0}t||krRt ||}| |}|j |dq8t d}|r| d}t|d}|D]*} | |vr|jt|j | |d| fdqdS) ztest hash() honors salt_charsrruÿrvrXzinvalid salt char %r:)rrN)rrMrrrrcbytesrrrrrdrrprErr) rjrMmxrRcsrawrr{chunkrr.r.r/test_14_salt_charss(      zHandlerCase.test_14_salt_charscCst|jddrtStSdS)z)hack to determine salt keyword's datatypeZ_salt_is_bytesFN)rrMrrrr.r.r/ salt_typeszHandlerCase.salt_typecCs||j}t|jddpd}Gdddt}|jt|jd|d|turj|jt|jdt d|d|t ust r~|tus|jt|jdd |dd S) ztest non-string salt valuesrrc@s eZdZdS)z+HandlerCase.test_15_salt_type..fakeN)rrrr.r.r.r/fakesrrtrxxN) rrrrMobjectrrr[rrrr )rjrrrr.r.r/test_15_salt_typeszHandlerCase.test_15_salt_typecCs||j}|j}|j}|j}|jt|jdd|t g|jddd}Wdn1sb0Y| |j||r|jt|j|dd|t g"|j|ddd}Wdn1s0Y| |j|||krJ|j|dd}| |j|d| |j||j|dd}| |j|d| |j|||krZ|}n|d}|jt |d}| |j||jt|jt |dd|j|d }| |j|dS) z$Handler.using() -- default_salt_sizer)rT)rrGNrXrxxxr) rrMrrrrrrWrrrrw)rjrMrRrrStemprefr.r.r/test_using_salt_sizes:,0   z HandlerCase.test_using_salt_sizecCst|js|ddS)Nzhandler lacks rounds attributes)rrMrrr.r.r/require_rounds_info7s zHandlerCase.require_rounds_infocCs||j}|j}|jdur&|d|jdkr8|d|jdkrJ|d|j|jkr^|d|jdur|j|jkr||d|j|jkr|d |jtvr|d |jfdS) z#validate optional rounds attributesNzmax_rounds not specifiedrXzmax_rounds must be >= 1rzmin_rounds must be >= 0z min_rounds must be <= max_roundsz$default_rounds must be >= min_roundsz$default_rounds must be <= max_roundsz unknown rounds cost constant: %r)rrMr max_roundsrirQrOr)rjrrr.r.r/"test_20_optional_rounds_attributes;s$        z.HandlerCase.test_20_optional_rounds_attributescCs`||j}|j}|j|d|jd|d|jt|j|dd|jt|jd|dddS)z+test hash() / genconfig() honors min_roundsrOrtrXN)rrMrirar[rr)rjrMrir.r.r/test_21_min_roundsYs zHandlerCase.test_21_min_roundscCsp||j}|j}|durJ|jt|j|dd|jt|jd|dd|dur`|jddn |j|ddS)z+test hash() / genconfig() honors max_roundsNrXrrti)rrMrrrrar[rd)rjrMrr.r.r/test_21b_max_roundsiszHandlerCase.test_21b_max_roundsc s||j}|jdkr8||}tfdd|_|j}|j}|j}|pPd|d}||krj|d7}||d}|p|d|d}|jdkr|dO}|dO}|dO}d}nd}| g |j|||d} Wdn1s0Y|| ||||fS) zU setup test helpers for testing handler.using()'s rounds parameters. Z bsdi_cryptcst|Sr1)r_generate_roundsr)r orig_handlerr.r/rr:z9HandlerCase._create_using_rounds_helper..i'rrX)min_desired_roundsmax_desired_roundsrQN) rrMrrW classmethodrrirrQr) rjrMorig_min_roundsorig_max_roundsorig_default_roundsmediumsmalllargeadjrjr)rr/_create_using_rounds_helpers6    $z'HandlerCase._create_using_rounds_helperc Cs||j}|j}|j}|j}|\}}}}}} ||j|||j|||jd||jd||j|||j|||j|||j|||j|||j|dS)z@ HasRounds.using() -- sanity check test harness N) rrMrirrQrrrr) rjrMrrrrjrrrrr.r.r/test_has_rounds_using_harnesss z)HandlerCase.test_has_rounds_using_harnessc Cs|\}}}}}}|j}|j}|j} |dkr|jt|j||d|tg"|j||dd} Wdn1sx0Y| | j ||r|jt|j||d|tg"|j||dd} Wdn1s0Y| | j ||g |j||d} Wdn1s(0Y| | j |||j|d|d} | | j |d||g |j||d} Wdn1s0Y| | j ||| t |||||| t ||||g*| t |||||Wdn1s$0Y|j|d} | | j ||jt |d} | | j ||jt|jt |dddS) zF HasRounds.using() -- min_rounds / min_desired_rounds rrT)rrGNrrir) rrirrQrrrWrrrrrRrw) rjrMrjrrrrrrrrr.r.r/"test_has_rounds_using_w_min_roundss>00 0 0 : z.HandlerCase.test_has_rounds_using_w_min_roundsc Cs|\}}}}}}|j}|j}|dkr|jt|j||d|tg"|j||dd} Wdn1sr0Y|| j ||r|jt|j||d|tg"|j||dd} Wdn1s0Y|| j ||t g |j||d} Wdn1s$0Y|| j ||jt|j||||d|j|d|d} || j |d||g |j||d} Wdn1s0Y|| j |||t ||||||t ||||g*|t |||||Wdn1s80Y|j|d } || j ||jt |d} || j ||jt|jt |d ddS) zF HasRounds.using() -- max_rounds / max_desired_rounds r)rT)rrGNrrrrrr)rrirrrrWrrrrrrrRrw) rjrMrjrrrrrrrr.r.r/$test_has_rounds_replace_w_max_roundssD000  0 : z0HandlerCase.test_has_rounds_replace_w_max_roundsc Cs|\}}}}}}|j}|j||d}||j|||j||d}||j|||jt|j||d|r|jt|j||d|t|||t||||||jt|d}||j||jt|jt|dddS)z5 HasRounds.using() -- default_rounds rr)rQrN) rrrWrrQrrrRrw rjrMrjrrrrrrr.r.r/&test_has_rounds_using_w_default_rounds8sz2HandlerCase.test_has_rounds_using_w_default_roundsc Cs|\}}}}}}|j}|j||d}||j||||j||||j|||j|d|||||d}||j||||j|||j||dS)z- HasRounds.using() -- rounds rrX)rOrirQrN)rrrWrrrQrrr.r.r/test_has_rounds_using_w_rounds^sz*HandlerCase.test_has_rounds_using_w_roundscs|\}}}}}fdd}||dd||dd||dd||dd||dd|t|d|t|d d S) z: HasRounds.using() -- vary_rounds parsing csj|djS)N vary_rounds)rWrrrjr.r/parsezszFHandlerCase.test_has_rounds_using_w_vary_rounds_parsing..parser'z0.1z10%Z1000gg?N)rrrr)rjrMrrrrrr.rr/+test_has_rounds_using_w_vary_rounds_parsingss z7HandlerCase.test_has_rounds_using_w_vary_rounds_parsingc s\}}}}}ddfdd}|d|||d|||||||||dt||dt||d|jdkr|d |||d |||d |||nljd d \}}|t||d |t||d|t||d|t||ddS)z= HasRounds.using() -- vary_rounds generation cs*tfddtdD}t|t|fS)Nc3s|]}tVqdSr1)rRr6rr.r/r9r:zjHandlerCase.test_has_rounds_using_w_vary_rounds_generation..get_effective_range..r)setrr<rE)rseenr.rr/get_effective_rangeszWHandlerCase.test_has_rounds_using_w_vary_rounds_generation..get_effective_rangecs8j|d}|\}}||d||ddS)Nrz"vary_rounds had wrong lower limit:z"vary_rounds had wrong upper limit:)rWr)rrrrZ seen_lowerZ seen_upperrrjrjr.r/assert_rounds_ranges  zWHandlerCase.test_has_rounds_using_w_vary_rounds_generation..assert_rounds_rangerz0%2rPz1%z49%z50%r?g?g333333?g?N)rrEr<rOrWassertGreaterEqualZassertLessEqual) rjrMrrrrrrrr.rr/.test_has_rounds_using_w_vary_rounds_generations       z:HandlerCase.test_has_rounds_using_w_vary_rounds_generationc Cs|\}}}}}}|j|d|dd}|j||d}|j||d} |j||d} |||||| ||| |||||| ||| dS)zF HasRounds.using() -- desired_rounds + needs_update() rrrN)rrWrd assertFalseZ needs_updaterI) rjrMrjrrrrrZ small_hashZ medium_hashZ large_hashr.r.r/&test_has_rounds_using_and_needs_updatesz2HandlerCase.test_has_rounds_using_and_needs_updatecCs*|j}t|trt|tjs&|ddS)Nz)handler doesn't derive from HasManyIdents)rMrcrerJrKZ HasManyIdentsr)rjrMr.r.r/require_many_identsszHandlerCase.require_many_identscCs6|j}||d|jv|jD]}||tdq$|t|jdkd||jtd||j|jvd|j rt |j D]<\}}||td||td|||jvd |fq|}| d}| |}|d=|fd|ji||j t|fi||fd d i||j t|fdd i|d S)z$validate HasManyIdents configurationidentz!cls.ident_values must be unicode:rXz'cls.ident_values must have 2+ elements:z"cls.default_ident must be unicode:z9cls.default_ident must specify member of cls.ident_valuesz'cls.ident_aliases keys must be unicode:z)cls.ident_aliases values must be unicode:z:cls.ident_aliases must map to cls.ident_values members: %rrPTxXxN)rMrrIrI ident_valuesrrr default_ident ident_aliasesrrG parsehashrrr)rjrraliasrrMrBr`r.r.r/test_30_HasManyIdentssF     z!HandlerCase.test_30_HasManyIdentscCs6||j}|j}|jD]}||krq>qtd||jfdd}|}||j||j|d}||j|||j||||||||||jt|jdd|j|d}||j|||j||jt |j||d|j r2|j D],\}}|j|d}|j|j|d|d qd S) z=HasManyIdents.using() -- 'default_ident' and 'ident' keywordsz6expected to find alternate ident: default=%r values=%rcSst|}|ddjS)NT)rP)rQrrr.r.r/effective_identsz?HandlerCase.test_has_many_idents_using..effective_ident)rr)r)rrz alias %r:rN) rrMrrrrWrrrrrr)rjrMZ orig_identZ alt_identrrjrrr.r.r/test_has_many_idents_usings4    z&HandlerCase.test_has_many_idents_usingcs|jjdur"|djdSjs4|jdjvrN|jdSfdd}||dj||dd||dd||dd||dd| t |d dS) zH validate 'truncate_error' setting & related attributes Ntruncate_errorcsj|djS)Nr)rWrrhasherr.r/ parse_valueSsz.parse_valueTtrueFZfalser) rM truncate_sizerrIrrtruncate_verify_rejectrIrrr)rjrr.rr/test_truncate_error_setting;s      z'HandlerCase.test_truncate_error_settingcCs|j}|jdur(||jd|dd}d}||}|j }|j||||dd|dd|}||||d dS) zO test no password size limits enforced (if truncate_size=None) NrXztruncate_size is setatoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretstoo many secretsrzverify rejected correct secretrrz full password not used in digest) rMrrrr[rJrrHr)rjrrArDrBZverify_successZ alt_secretr.r.r/test_secret_wo_truncate_size\s   z(HandlerCase.test_secret_wo_truncate_sizecCs|j}|j}|s|dtj}d|jvrJ|jdd}|jdd}tj}n|jrZd}|}n|}d}d}d}t ||d }|dd } |dd |} | dd |} |j } | o|j } |s|sJ||fD]Z}|j | |d }| |j| ||d | ||j| ||d d | |j|||d | q|r|j ||d }| |j|||d | | |j| ||d | ||j| ||d |r|j||j ||d }| |j|dS) zQ test password size limits raise truncate_error (if appropriate) ztruncate_size not setrFrTNztoo many secretsrrXrrLz truncate_size value is too large)rMrrr PasswordSizeErrorrIrWZPasswordTruncateErrorrrrJrr[rrHrrIrr)rjrMrZsize_error_typeZ without_errorZ with_errorrgrDZ long_secretZ short_secretZalt_long_secretZalt_short_secretZshort_verify_successZlong_verify_successZ cand_hasherZ short_hashZ long_hashrr.r.r/test_secret_w_truncate_size|s|              z'HandlerCase.test_secret_w_truncate_sizecCs|jdu}|jdv}d}d}||}|rH|jjsH||||dn||||d||}|r|jjs||||dn||||d|||}|s|jjr|js| ||dn| ||dd S) ztest password case sensitivityT)Tz verify-onlyr:ZTESTz%verify() should not be case sensitivez!verify() should be case sensitivez&genhash() should not be case sensitivez"genhash() should be case sensitiveN) secret_case_insensitiver[rMrJrIrHrrcrxrry)rjZhash_insensitiveZverify_insensitiverrh1h2r.r.r/test_61_secret_case_sensitives8       z)HandlerCase.test_61_secret_case_sensitivecCsx|d}|t|jd|t|jd||t|jd||t|jd|t|jd||t|jd|dS)z&test non-string passwords are rejectedrXN)rGrrr[rcrHr^r.r.r/test_62_secret_border s z!HandlerCase.test_62_secret_bordercCsvddlm}ddlm}dd|}|d}|||j||}||j||||j ||||j ||dS)z"test MAX_PASSWORD_SIZE is enforcedr)r)MAX_PASSWORD_SIZEr/rXN) passlib.excrrrrGrrcrrr[rH)rjrrrArBrr.r.r/test_63_large_secret s    z HandlerCase.test_63_large_secretcCsj|j}|s|dtd}t|trDddlm}||}|d}|D]}|t |j |||qHdS)z1test forbidden characters not allowed in passwordz none listedrtr)iter_byte_charsasciiN) r=rrrcrpasslib.utils.compatrrprrr[)rjcharsrgrrr.r.r/test_64_forbidden_chars) s    z#HandlerCase.test_64_forbidden_charscCs||i}t| Sr1)rUrrjrAr.r.r/is_secret_8bit9 s zHandlerCase.is_secret_8bitcCs@tr<|jdkrr?rErr_r rLrcrrwrMrJrxrr r r)rjZsaw8bitrArBr rr.r.r/test_70_hashesH s:         zHandlerCase.test_70_hashesc Cs|js|d|jD]\}}}|||d|f|||d||f|||}||td|f|jj r~|j r~q| ||d||||fqdS)ztest known alternate hasheszno alternate hashes providedz0identify() failed to identify alternate hash: %rz;verify() of known alternate hash failed: secret=%r, hash=%rr zYgenhash() failed to normalize known alternate hash: secret=%r, alt=%r, hash=%r: result=%rN) r@rrIr_rLrcrrwrMrJrxr)rjrDrArBrr.r.r/test_71_alternatest s(      zHandlerCase.test_71_alternatesc Cs|jjs ||jd|d|js0|d|jrDtjdtd|jD]x\}}}| | |d|f|j t |j ||d|fd|||}||td |f|||d ||||fqJd S) ztest known config stringsz&handler should not have config stringszhash has no settingszno config strings providedrrz5identify() failed to identify known config string: %rz+verify() failed to reject config string: %rrr z^genhash() failed to reproduce known hash from config: secret=%r, config=%r, hash=%r: result=%rN)rMrIrr?rfilter_config_warningsrrrrIr_rrrHrcrrwr)rjrCrArBrr.r.r/test_72_configs s6      zHandlerCase.test_72_configscCsp|js|d|jD]T}|||d|f|jt|jd|d|fd|jt|jd|d|fdqdS)z)test known unidentifiably-mangled stringszno unidentified hashes providedz?identify() incorrectly identified known unidentifiable hash: %rrtz:verify() failed to throw error for unidentifiable hash: %rr z;genhash() failed to throw error for unidentifiable hash: %rN)known_unidentified_hashesrrr_rrrHrcr^r.r.r/test_73_unidentified s$   z HandlerCase.test_73_unidentifiedcCsp|js|d|jD]T}|||d|f|jt|jd|d|fd|jt|jd|d|fdqdS)z-test known identifiable-but-malformed stringszno malformed hashes providedz6identify() failed to identify known malformed hash: %rrtz5verify() failed to throw error for malformed hash: %rr z6genhash() failed to throw error for malformed hash: %rN)known_malformed_hashesrrIr_rrrHrcr^r.r.r/test_74_malformed s$   zHandlerCase.test_74_malformedc Cs|jr|d|js |d|jD]\}}||jjkr|||d|f|d||d|}| |t d|fq&| ||d||f|j t |jd|d||fd|j t |jd|d ||fdq&d S) ztest known foreign hashesnot applicablezno foreign hashes providedz,identify() failed to identify known hash: %rrtr z:identify() incorrectly identified hash belonging to %s: %rz;verify() failed to throw error for hash belonging to %s: %rr z.vnamez fuzz testrpz.%s: %s: started; max_time=%r verifiers=%d (%s)rc3s|]}|VqdSr1r.)r7r)r*r.r/r9 r:z1HandlerCase.test_77_fuzz_input..rAr{rZrYrTFzAfailed to verify against %r verifier: secret=%r config=%r hash=%rr'z\was able to verify wrong password using %s: wrong_secret=%r real_secret=%r config=%r hash=%rrXz!%s: %s: done; elapsed=%r count=%rN)rMrJrrr max_fuzz_timeget_fuzz_verifiersr2current_threadrrFuzzHashGeneratorrdebugrrrgenerater[rr)rjr'rZmax_time verifiersZ thread_namer generatorr2stoprZoptsrAr{rZrrBr\rrr.r+r/test_77_fuzz_input| sb            zHandlerCase.test_77_fuzz_inputcs dddljjr$dj}|dks<jdkrFddgfddfd d fd d t|D}j|d }d}|D]0}| || sqt d|j ||d7}qdrdd|f|rd||fdS)zmultithreaded fuzz testing -- random password & options using multiple threads run test_77 simultaneously in multiple threads in an attempt to detect any concurrency issues (e.g. the bug fixed by pybcrypt 0.3) rArNrrXr%c shzjddWnRty"YnB dd7<Wdn1sR0YYn0dS)NTr&rrX)r5rr.)failed failed_lockrjr.r/rl s .z3HandlerCase.test_78_fuzz_threading..wrappercsBt}d||j|jjf}j|d}|d||S)NzFuzz-Thread-%d ('%s:%s.%s'))targetrT)rerrr Thread setDaemonr2)nrrthread)rjr2rlr.r/launch s  z2HandlerCase.test_78_fuzz_threading..launchcsg|] }|qSr.r.)r7r;)r=r.r/ r:z6HandlerCase.test_78_fuzz_threading..r z%s timed out after %f secondszH%d/%d threads failed concurrent fuzz testing (see error log for details)zP%d/%d threads stalled during concurrent fuzz testing (see error log for details))rr2rMrJrfuzz_thread_countr,r3rris_alivererrorrr")rjZ thread_countthreadstimeoutZstalledr<r.)r6r7r=rjr2rlr/test_78_fuzz_threading s8       z"HandlerCase.test_78_fuzz_threadingcCs@ttjdpd}|r|Stddr*dStddr8dSdSdS) z'amount of time to spend on fuzz testingZPASSLIB_TEST_FUZZ_TIMErr?rNr@rXN)floatr(rrr#rjrr.r.r/r, s  zHandlerCase.max_fuzz_timecCs2ttjdpd}|r|Stddr*dSdSdS)z+number of threads for threaded fuzz testingZPASSLIB_TEST_FUZZ_THREADSrr?rNrN)rr(rrr#rGr.r.r/r?$ s  zHandlerCase.fuzz_thread_count)fuzz_verifier_defaultcsv|jg}|jD]"}t||}|dur||qtdrrtdrr|srfdd}tD]}|||q^|S)zreturn list of password verifiers (including external libs) used by fuzz testing. verifiers should be callable with signature ``func(password: unicode, hash: ascii str) -> ok: bool``. NrYrAcs*fdd}dd|_d|_|S)Nc s<}z"||W|S|0dSr1)rSrTr\)rArBZ orig_backend)rUrMr.r/rM s   z;HandlerCase.get_fuzz_verifiers..maker..funcZcheck_Z_backend-backend)rr)rUrrL)rUr/makerL s z-HandlerCase.get_fuzz_verifiers..maker)rMfuzz_verifiersrr$rHr#r^)rjr'r2Z method_namerrJrUr.rLr/r-8 s    zHandlerCase.get_fuzz_verifierscs*fdd}jr jd|_nd|_|S)Ncsj||fi|Sr1)rH)rArBrrr.r/ check_default^ sz8HandlerCase.fuzz_verifier_default..check_defaultrIrj)rUr)rjrLr.rr/rH\ s  z!HandlerCase.fuzz_verifier_defaultc@sveZdZdZedZdZeddddZiZ dd Z d d Z d d Z ddZ ddZddZddZddZddZdS)zHandlerCase.FuzzHashGeneratorz helper which takes care of generating random passwords & configuration options to test hash with. separate from test class so we can create one per thread. uqwertyASDF1234<>.@*#! áəБℓrn random_roundsrandom_salt_size random_ident)rOrrcCs||_|j|_||_dSr1)r:rMr)rjr:rr.r.r/r sz&HandlerCase.FuzzHashGenerator.__init__cs4fdd}\}}t|||j|jdS)z generate random password and options for fuzz testing. :returns: `(secret, other_secret, settings_kwds, context_kwds)` cs6i}|D]$\}}t|}|dur |||<q |Sr1)rr)maprrrlrrr.r/gendict s   z7HandlerCase.FuzzHashGenerator.generate..gendict)rAr{rZrY)random_password_pairr settings_map context_map)rjrQrAr{r.rr/r1 s  z&HandlerCase.FuzzHashGenerator.generatecCs|j||}tt|||S)z)generate random int w/ gauss distirbution)rZ normalvariaterr)rjrrZmuZsigmarr.r.r/ randintgauss sz*HandlerCase.FuzzHashGenerator.randintgausscCsX|j}t|sdS|jp|j}|j}|jdkr4|}nt|d|j}|||||dS)NrPrr)rMrrQrirOr<rrUrjrMr@rrr.r.r/rM s  z+HandlerCase.FuzzHashGenerator.random_roundscCsJ|j}t|rd|jvsdS|j}|j}|jp4|d}|||||dS)Nrr r)rMrrIrrrrUrVr.r.r/rN sz.HandlerCase.FuzzHashGenerator.random_salt_sizecCsL|j}|j}d|jvs t|ds$dS|dkr4dSt|d|}||jS)Nrrrrb)rrMrIrHrrrFr)rjrrMr.r.r/rO s  z*HandlerCase.FuzzHashGenerator.random_identcCs^|}|}|||rq q|j}|ddr>||j}|ddrV||j}||fS)z=generate random password, and non-matching alternate passwordrrX)random_passwordaccept_password_pairrrandintrppassword_encoding)rjrAr{rr.r.r/rR s     z2HandlerCase.FuzzHashGenerator.random_password_paircCs|j}|dkrtdS|j}|jo*|j}|p2d}|dksH|dkr`|dt|ddd}n|dt|dd d }t||j |}|rt |t rt | d |kr|d d }q|S)z*generate random passwords for fuzz testingg-C6?r}i?BrrrXcFrnNr)rrrrMrrrUr<rpassword_alphabetrcrrrp)rjrrMrrsizerr.r.r/rW s  z-HandlerCase.FuzzHashGenerator.random_passwordcCs||kS)z-verify fuzz pair contains different passwordsr.)rjrAr{r.r.r/rX sz2HandlerCase.FuzzHashGenerator.accept_password_pairN)rrrrrr_rZrrSrTrr1rUrMrNrOrRrWrXr.r.r.r/r/i s"    r/c Cs|j}|jsB|t|d|t|d||j|d|}|j|tdd|j | |d|d| |j d}||}|j|tdd|j | |d|d|td|j|z||}d }Wn.ty }zd }|}WYd }~n d }~00|d ur4|j|td d|||n||t|dt||}|jrn|||n|d ur|||||} |jr|| |n || |||d } |js|d ur|| |n || |d S) z.disable() / .enable() methodsdisableenablerz#disable() must return native stringrz0identify() didn't recognize disable() result: %rrXzcannot restore original hashNz"enable() must return native stringr)rMrJrrHrxrrarrwrIr]rrFrZassertRaisesRegexrrbrrry) rjrMZdisabled_defaultrtZ disabled_stubrrAeZdisabled_default2Zdisabled_stub2Zdisabled_otherr.r.r/test_disable_and_enable sd            z#HandlerCase.test_disable_and_enable)NF)FNN)N)NN)F)F)F)nrrrrrMrUr>r?r@rrrrrwrrrxrr"r=Z_HandlerCase__unittest_skippropertyrrrErGrLrMrTrUr[rHr_rarcrdrerfrkrgrmrrsrurvr|r}r~rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr r r rrrrrr#rrrr$r5rDr,r?rKr-rHrr/rdrr.r.rr/r&s            ! ) 7#,) 6 2 +8<&)34! m&  ,#&0' YB   $ r&cseZdZdZgZdZdZdZfddZe ddZ e d d Z d d Z e fd dZddZddZddZeddZddZddZZS)rhahelper used by create_backend_case() which adds additional features to test the os_crypt backend. * if crypt support is missing, inserts fake crypt support to simulate a working safe_crypt, to test passlib's codepath as fully as possible. * extra tests to verify non-conformant crypt implementations are handled correctly. * check that native crypt support is detected correctly for known platforms. Tr;Fcs4|jdksJ|jds"|tt|dS)Nr;)rUrMrZ_patch_safe_cryptrrhrrrr.r/r s zOsCryptMixin.setUpcCs$t|j}|t|d}||fS)z return (handler, backend) pair to use for faking crypt.crypt() support for hash. backend will be None if none availabe. r;)rQrMrSra)rrM alt_backendr.r.r/_get_safe_crypt_handler_backend s  z,OsCryptMixin._get_safe_crypt_handler_backendcCs|dduS)z test if there's a fallback handler to test against if os_crypt can't support a specified secret (may be explicitly set to False for some subclasses) rN)rhrr.r.r/has_os_crypt_fallback sz"OsCryptMixin.has_os_crypt_fallbackcsZ|\}}|std||fdd}ddlm}||d|d|_dS)zif crypt() doesn't support current hash alg, this patches safe_crypt() so that it transparently uses another one of the handler's backends, so that we can go ahead and test as much of code path as possible. z,handler has no available alternate backends!cs||}t|tsJ|Sr1)rbrcrwrArBZ alt_handlerr.r/ crypt_stub s z2OsCryptMixin._patch_safe_crypt..crypt_stubrN_cryptT)rhrrWrTrutilsrusing_patched_crypt)rjrMrgrlmodr.rkr/rf s    zOsCryptMixin._patch_safe_cryptcsV|dks Jtt||}ddlm}||jkrR|rRtdrN|drNdSdS|S)z| make sure os_crypt backend is tested when it's known os_crypt will be faked by _patch_safe_crypt() r;r) has_cryptrArXNz hash not supported by os crypt())rrhrfrrqrer#rh)rrUreasonrqrr.r/rf s  z%OsCryptMixin._get_skip_backend_reasoncs8ddlm}fdd|j_d_||dS)z patch passlib.utils.safe_crypt() so it returns mock value for duration of test. returns function whose .return_value controls what's returned. this defaults to None. rNcs|dkr||SjSdS)Nr:) __wrapped__ return_value)rArC mock_cryptr.r/rv s z0OsCryptMixin._use_mock_crypt..mock_cryptrm)rrnrmrsrtr)rjrpr.rur/_use_mock_crypt s   zOsCryptMixin._use_mock_cryptcsbdtjffdd}|ddd|dd|ddS) ztest with faulty crypt()rXcs>|_jdjdjddSr)rtrrcr[rHrZ exc_typesrBrvrjr.r/r: sz/OsCryptMixin.test_80_faulty_crypt..testz$xrNrr)rGr InternalBackendErrorrw)rjr:r.rxr/test_80_faulty_crypt s z!OsCryptMixin.test_80_faulty_cryptcCs|}d|_|jrJ|d}|d|}|||||d|nLddlm }| d}| ||jd| ||jd|| ||jd|dS)ztest per-call crypt() fallbackNrtr)ryrX) rwrtrir[rcrrIrHrryrGr)rjrvrrZerr_typerBr.r.r/test_81_crypt_fallback s     z#OsCryptMixin.test_81_crypt_fallbackcCst|jdr|d|j }|jj}tj}|jD]\}}t ||r2q^q2|d||f|durx|d||f||krn*|r| d||fn| d||fdS)a' test platform-specific crypt() support detection NOTE: this is mainly just a sanity check to ensure the runtime detection is functioning correctly on some known platforms, so that we can feel more confident it'll work right on unknown ones. rFznot applicable to wrappersz3no data for %r platform (current host support = %r)Nz9varied support on %r platform (current host support = %r)z5expected %r platform would have native support for %rz;did not expect %r platform would have native support for %r) rHrMrrorsysplatformplatform_crypt_supportr~rrr")rjZ using_backendrr}patternZexpectedr.r.r/test_82_crypt_support s0   z"OsCryptMixin.test_82_crypt_supportcsPj}jst|drdSddlmddlmjjfdd}|S)ztest results against OS crypt()rbNr)crypt)_safe_crypt_lockcsN|sdSt|}|||kWdS1s@0YdS)z stdlib-cryptrN)crypt_supports_variantrrjrrencodingrjr.r/ check_cryptS s   z5OsCryptMixin.fuzz_verifier_crypt..check_crypt)rMrorHrrrr/rZ)rjrMrr.rr/fuzz_verifier_cryptE s   z OsCryptMixin.fuzz_verifier_cryptcCsdS)z~ fuzzy_verified_crypt() helper -- used to determine if os crypt() supports a particular hash variant. Tr.r^r.r.r/r^ sz#OsCryptMixin.crypt_supports_variant)rrrrr~Z_OsCryptMixin__unittest_skiprUrorrrhrerirfrfrwrzr{r9rrrrr.r.rr/rhf s(    /rhc@sReZdZdZdZdZdZdZddZddZ d d Z d d Z Gd dde j Z dS)UserHandlerMixina(helper for handlers w/ 'user' context kwd; mixin for HandlerCase this overrides the HandlerCase test harness methods so that a username is automatically inserted to hash/verify calls. as well, passing in a pair of strings as the password will be interpreted as (secret,user) userTFcCs||j}d}|j||jd}|jrV|t|j||t|j|||t|j||n"||||||||dS)ztest user context keywordrtrN)rMrB default_user requires_userrrrbr\)rjrMpasswordrBr.r.r/ test_80_user s  zUserHandlerMixin.test_80_usercCsb|j}|}|jdt|dd}|jrF||jd||ddn||jd||dddS)ztest user case sensitivityrtr)rYz!user should not be case sensitivezuser should be case sensitiveN) rrrr[ruser_case_insensitiverIrHr)rjrrrBr.r.r/test_81_user_case s z"UserHandlerMixin.test_81_user_casecCsT|}|jd|dd}|jd|dd}||||jd|dd}|||dS)ztest user used as saltrtZadminrrootN)rdrcrry)rjrCrrZh3r.r.r/test_82_user_salt s  z"UserHandlerMixin.test_82_user_saltcCs8t|tr|\}}n|js|S|j}d|vr4||d<|S)zinsert username into kwdsr)rcrrr)rjrAr`rr.r.r/rU s  z!UserHandlerMixin.populate_contextc@s4eZdZejjZejddedZ ddZ dS)z"UserHandlerMixin.FuzzHashGenerator random_userrZ asdQWE123cCs4|j}|jjs|dkrdSt||j|ddS)Nr'rr)rr:rrr user_alphabetrY)rjrr.r.r/r sz.UserHandlerMixin.FuzzHashGenerator.random_userN) rrrr&r/rTrrVrrrr.r.r.r/r/ s  r/N)rrrrrrrZ _UserHandlerMixin__unittest_skiprrrrUr&r/r.r.r.r/ri s  rc@s@eZdZdZdZeddedgZGdddejZdd Z d S) EncodingHandlerMixina helper for handlers w/ 'encoding' context kwd; mixin for HandlerCase this overrides the HandlerCase test harness methods so that an encoding can be inserted to hash/verify calls by passing in a pair of strings as the password will be interpreted as (secret,encoding) Tr:stestu¬ºc@seZdZedZdS)z&EncodingHandlerMixin.FuzzHashGeneratoruqwerty1234<>.@*#! ¬N)rrrrr_r.r.r.r/r/ sr/cCs"t|tr|\}}|d||S)zinsert encoding into kwdsr)rcr setdefault)rjrAr`rr.r.r/rU s  z%EncodingHandlerMixin.populate_contextN) rrrrZ$_EncodingHandlerMixin__unittest_skiprrwr&r/rUr.r.r.r/r s rcs:eZdZdZd fdd ZfddZfdd ZZS) rz@catch_warnings() wrapper which clears warning registry & filtersalways.*c s4tt|jfi|||_|r*t|nd|_dSr1)rrr _reset_filterr~compile_reset_registry)rjZ reset_filterZreset_registryr`rr.r/r szreset_warnings.__init__cstt|}|jr(tt|j|j}|ri}|_t t j D]@\}}|dusJ| |sfqJt|dd}|rJ|||<|qJ|SNZ__warningregistry__)rrrrr resetwarnings simplefilterr_orig_registryrr|modulesrrrrrclear)rjrrbackuprrpregrr.r/r s     zreset_warnings.__enter__cs|j}|r|j}ttjD]b\}}|dus||s:qt|dd}|rR|| |}|r|durvt |d|q| |qt t |j|dSr)rrrr|rrrrrrrr-rVrrr)rjrrrrrprrVrr.r/rs   zreset_warnings.__exit__)rrrr.r.rr/r s r)NN)N)NF)NrXr)rr)jrZ __future__rZbinasciirr5 functoolsrrr r0rrrrr~r(r|r!r2r+rrrrrr rr Zpasslibr r Zpasslib.registryregistryZpasslib.tests.backportsr r4rrrrrrrrrr rrrrrrrrrrr r!Zpasslib.utils.decorr"Zpasslib.utils.handlersrnrnrK__all__Zgoogle.appengineZgoogle ImportErrorrr0r>ZTICK_RESOLUTIONrCrDrrrrrBr#rNrRrWr^rarQrhr6rmr$r%r|rrrrrrr9r&rhrrr1rr.r.r.r/s      0             F ya&