ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Tests for L{twisted.cred}, now with 30% more starch. """ from __future__ import absolute_import, division from zope.interface import implementer, Interface from binascii import hexlify, unhexlify from twisted.trial import unittest from twisted.python.compat import nativeString, networkString from twisted.python import components from twisted.internet import defer from twisted.cred import checkers, credentials, portal, error try: from crypt import crypt except ImportError: crypt = None class ITestable(Interface): """ An interface for a theoretical protocol. """ pass class TestAvatar(object): """ A test avatar. """ def __init__(self, name): self.name = name self.loggedIn = False self.loggedOut = False def login(self): assert not self.loggedIn self.loggedIn = True def logout(self): self.loggedOut = True @implementer(ITestable) class Testable(components.Adapter): """ A theoretical protocol for testing. """ pass components.registerAdapter(Testable, TestAvatar, ITestable) class IDerivedCredentials(credentials.IUsernamePassword): pass @implementer(IDerivedCredentials, ITestable) class DerivedCredentials(object): def __init__(self, username, password): self.username = username self.password = password def checkPassword(self, password): return password == self.password @implementer(portal.IRealm) class TestRealm(object): """ A basic test realm. """ def __init__(self): self.avatars = {} def requestAvatar(self, avatarId, mind, *interfaces): if avatarId in self.avatars: avatar = self.avatars[avatarId] else: avatar = TestAvatar(avatarId) self.avatars[avatarId] = avatar avatar.login() return (interfaces[0], interfaces[0](avatar), avatar.logout) class CredTests(unittest.TestCase): """ Tests for the meat of L{twisted.cred} -- realms, portals, avatars, and checkers. """ def setUp(self): self.realm = TestRealm() self.portal = portal.Portal(self.realm) self.checker = checkers.InMemoryUsernamePasswordDatabaseDontUse() self.checker.addUser(b"bob", b"hello") self.portal.registerChecker(self.checker) def test_listCheckers(self): """ The checkers in a portal can check only certain types of credentials. Since this portal has L{checkers.InMemoryUsernamePasswordDatabaseDontUse} registered, it """ expected = [credentials.IUsernamePassword, credentials.IUsernameHashedPassword] got = self.portal.listCredentialsInterfaces() self.assertEqual(sorted(got), sorted(expected)) def test_basicLogin(self): """ Calling C{login} on a portal with correct credentials and an interface that the portal's realm supports works. """ login = self.successResultOf(self.portal.login( credentials.UsernamePassword(b"bob", b"hello"), self, ITestable)) iface, impl, logout = login # whitebox self.assertEqual(iface, ITestable) self.assertTrue(iface.providedBy(impl), "%s does not implement %s" % (impl, iface)) # greybox self.assertTrue(impl.original.loggedIn) self.assertTrue(not impl.original.loggedOut) logout() self.assertTrue(impl.original.loggedOut) def test_derivedInterface(self): """ Logging in with correct derived credentials and an interface that the portal's realm supports works. """ login = self.successResultOf(self.portal.login( DerivedCredentials(b"bob", b"hello"), self, ITestable)) iface, impl, logout = login # whitebox self.assertEqual(iface, ITestable) self.assertTrue(iface.providedBy(impl), "%s does not implement %s" % (impl, iface)) # greybox self.assertTrue(impl.original.loggedIn) self.assertTrue(not impl.original.loggedOut) logout() self.assertTrue(impl.original.loggedOut) def test_failedLoginPassword(self): """ Calling C{login} with incorrect credentials (in this case a wrong password) causes L{error.UnauthorizedLogin} to be raised. """ login = self.failureResultOf(self.portal.login( credentials.UsernamePassword(b"bob", b"h3llo"), self, ITestable)) self.assertTrue(login) self.assertEqual(error.UnauthorizedLogin, login.type) def test_failedLoginName(self): """ Calling C{login} with incorrect credentials (in this case no known user) causes L{error.UnauthorizedLogin} to be raised. """ login = self.failureResultOf(self.portal.login( credentials.UsernamePassword(b"jay", b"hello"), self, ITestable)) self.assertTrue(login) self.assertEqual(error.UnauthorizedLogin, login.type) class OnDiskDatabaseTests(unittest.TestCase): users = [ (b'user1', b'pass1'), (b'user2', b'pass2'), (b'user3', b'pass3'), ] def setUp(self): self.dbfile = self.mktemp() with open(self.dbfile, 'wb') as f: for (u, p) in self.users: f.write(u + b":" + p + b"\n") def test_getUserNonexistentDatabase(self): """ A missing db file will cause a permanent rejection of authorization attempts. """ self.db = checkers.FilePasswordDB('test_thisbetternoteverexist.db') self.assertRaises(error.UnauthorizedLogin, self.db.getUser, 'user') def testUserLookup(self): self.db = checkers.FilePasswordDB(self.dbfile) for (u, p) in self.users: self.assertRaises(KeyError, self.db.getUser, u.upper()) self.assertEqual(self.db.getUser(u), (u, p)) def testCaseInSensitivity(self): self.db = checkers.FilePasswordDB(self.dbfile, caseSensitive=False) for (u, p) in self.users: self.assertEqual(self.db.getUser(u.upper()), (u, p)) def testRequestAvatarId(self): self.db = checkers.FilePasswordDB(self.dbfile) creds = [credentials.UsernamePassword(u, p) for u, p in self.users] d = defer.gatherResults( [defer.maybeDeferred(self.db.requestAvatarId, c) for c in creds]) d.addCallback(self.assertEqual, [u for u, p in self.users]) return d def testRequestAvatarId_hashed(self): self.db = checkers.FilePasswordDB(self.dbfile) creds = [credentials.UsernameHashedPassword(u, p) for u, p in self.users] d = defer.gatherResults( [defer.maybeDeferred(self.db.requestAvatarId, c) for c in creds]) d.addCallback(self.assertEqual, [u for u, p in self.users]) return d class HashedPasswordOnDiskDatabaseTests(unittest.TestCase): users = [ (b'user1', b'pass1'), (b'user2', b'pass2'), (b'user3', b'pass3'), ] def setUp(self): dbfile = self.mktemp() self.db = checkers.FilePasswordDB(dbfile, hash=self.hash) with open(dbfile, 'wb') as f: for (u, p) in self.users: f.write(u + b":" + self.hash(u, p, u[:2]) + b"\n") r = TestRealm() self.port = portal.Portal(r) self.port.registerChecker(self.db) def hash(self, u, p, s): return networkString(crypt(nativeString(p), nativeString(s))) def testGoodCredentials(self): goodCreds = [credentials.UsernamePassword(u, p) for u, p in self.users] d = defer.gatherResults([self.db.requestAvatarId(c) for c in goodCreds]) d.addCallback(self.assertEqual, [u for u, p in self.users]) return d def testGoodCredentials_login(self): goodCreds = [credentials.UsernamePassword(u, p) for u, p in self.users] d = defer.gatherResults([self.port.login(c, None, ITestable) for c in goodCreds]) d.addCallback(lambda x: [a.original.name for i, a, l in x]) d.addCallback(self.assertEqual, [u for u, p in self.users]) return d def testBadCredentials(self): badCreds = [credentials.UsernamePassword(u, 'wrong password') for u, p in self.users] d = defer.DeferredList([self.port.login(c, None, ITestable) for c in badCreds], consumeErrors=True) d.addCallback(self._assertFailures, error.UnauthorizedLogin) return d def testHashedCredentials(self): hashedCreds = [credentials.UsernameHashedPassword( u, self.hash(None, p, u[:2])) for u, p in self.users] d = defer.DeferredList([self.port.login(c, None, ITestable) for c in hashedCreds], consumeErrors=True) d.addCallback(self._assertFailures, error.UnhandledCredentials) return d def _assertFailures(self, failures, *expectedFailures): for flag, failure in failures: self.assertEqual(flag, defer.FAILURE) failure.trap(*expectedFailures) return None if crypt is None: skip = "crypt module not available" class CheckersMixin(object): """ L{unittest.TestCase} mixin for testing that some checkers accept and deny specified credentials. Subclasses must provide - C{getCheckers} which returns a sequence of L{checkers.ICredentialChecker} - C{getGoodCredentials} which returns a list of 2-tuples of credential to check and avaterId to expect. - C{getBadCredentials} which returns a list of credentials which are expected to be unauthorized. """ @defer.inlineCallbacks def test_positive(self): """ The given credentials are accepted by all the checkers, and give the expected C{avatarID}s """ for chk in self.getCheckers(): for (cred, avatarId) in self.getGoodCredentials(): r = yield chk.requestAvatarId(cred) self.assertEqual(r, avatarId) @defer.inlineCallbacks def test_negative(self): """ The given credentials are rejected by all the checkers. """ for chk in self.getCheckers(): for cred in self.getBadCredentials(): d = chk.requestAvatarId(cred) yield self.assertFailure(d, error.UnauthorizedLogin) class HashlessFilePasswordDBMixin(object): credClass = credentials.UsernamePassword diskHash = None networkHash = staticmethod(lambda x: x) _validCredentials = [ (b'user1', b'password1'), (b'user2', b'password2'), (b'user3', b'password3')] def getGoodCredentials(self): for u, p in self._validCredentials: yield self.credClass(u, self.networkHash(p)), u def getBadCredentials(self): for u, p in [(b'user1', b'password3'), (b'user2', b'password1'), (b'bloof', b'blarf')]: yield self.credClass(u, self.networkHash(p)) def getCheckers(self): diskHash = self.diskHash or (lambda x: x) hashCheck = self.diskHash and (lambda username, password, stored: self.diskHash(password)) for cache in True, False: fn = self.mktemp() with open(fn, 'wb') as fObj: for u, p in self._validCredentials: fObj.write(u + b":" + diskHash(p) + b"\n") yield checkers.FilePasswordDB(fn, cache=cache, hash=hashCheck) fn = self.mktemp() with open(fn, 'wb') as fObj: for u, p in self._validCredentials: fObj.write(diskHash(p) + b' dingle dongle ' + u + b'\n') yield checkers.FilePasswordDB(fn, b' ', 3, 0, cache=cache, hash=hashCheck) fn = self.mktemp() with open(fn, 'wb') as fObj: for u, p in self._validCredentials: fObj.write(b'zip,zap,' + u.title() + b',zup,'\ + diskHash(p) + b'\n',) yield checkers.FilePasswordDB(fn, b',', 2, 4, False, cache=cache, hash=hashCheck) class LocallyHashedFilePasswordDBMixin(HashlessFilePasswordDBMixin): diskHash = staticmethod(lambda x: hexlify(x)) class NetworkHashedFilePasswordDBMixin(HashlessFilePasswordDBMixin): networkHash = staticmethod(lambda x: hexlify(x)) class credClass(credentials.UsernameHashedPassword): def checkPassword(self, password): return unhexlify(self.hashed) == password class HashlessFilePasswordDBCheckerTests(HashlessFilePasswordDBMixin, CheckersMixin, unittest.TestCase): pass class LocallyHashedFilePasswordDBCheckerTests(LocallyHashedFilePasswordDBMixin, CheckersMixin, unittest.TestCase): pass class NetworkHashedFilePasswordDBCheckerTests(NetworkHashedFilePasswordDBMixin, CheckersMixin, unittest.TestCase): pass