Skip to content

Commit

Permalink
Merge pull request #107 from jsza/36-hashed-passwords
Browse files Browse the repository at this point in the history
Implement password hashing
  • Loading branch information
mithrandi authored Aug 26, 2020
2 parents 12ffd46 + 6aa6f92 commit 38dae5e
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 96 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
branch = True
source =
axiom
omit =
*/axiom/test/historic/stub_*.py

[report]
exclude_lines =
Expand Down
Binary file added axiom/test/historic/account2to3.axiom.tbz2
Binary file not shown.
17 changes: 17 additions & 0 deletions axiom/test/historic/stub_account2to3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from axiom.userbase import LoginSystem
from axiom.dependency import installOn
from axiom.test.historic.stubloader import saveStub
from axiom.test.test_userbase import GarbageProtocolHandler


def createDatabase(s):
ls = LoginSystem(store=s)
installOn(ls, s)
acc = ls.addAccount(u'test', u'example.com', u'asdf')
ss = acc.avatars.open()
gph = GarbageProtocolHandler(store=ss, garbage=7)
installOn(gph, ss)


if __name__ == '__main__':
saveStub(createDatabase, 0x1240846306fcda3289550cdf9515b2c7111d2bac)
3 changes: 1 addition & 2 deletions axiom/test/historic/test_account1to2.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ def testUpgrade(self):

def loggedIn((ifc, av, lgo)):
assert av.garbage == 7
# Bug in cooperator? this triggers an exception.
# return svc.stopService()
return av.store.whenFullyUpgraded()
d = p.login(
UsernamePassword('[email protected]', SECRET), None, IGarbage)
return d.addCallback(loggedIn)
44 changes: 44 additions & 0 deletions axiom/test/historic/test_account2to3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from twisted.cred.portal import Portal, IRealm

from twisted.cred.checkers import ICredentialsChecker
from twisted.cred.credentials import UsernamePassword

from axiom.test.test_userbase import IGarbage
from axiom.test.historic import stubloader
from axiom.errors import BadCredentials
from axiom.userbase import getTestContext, LoginAccount

SECRET = 'asdf'
SECRET2 = 'ghjk'


class AccountUpgradeTest(stubloader.StubbedTest):
def test_upgrade(self):
"""
After the upgrade, logging in with the correct password succeeds, while
logging in with an incorrect password fails.
"""
ls = IRealm(self.store)
ls._txCryptContext, perform = getTestContext()
p = Portal(ls, [ICredentialsChecker(self.store)])
d = p.login(
UsernamePassword('[email protected]', SECRET), None, IGarbage)
perform()
(ifc, av, lgo) = self.successResultOf(d)
self.assertEqual(av.garbage, 7)

d = p.login(
UsernamePassword('[email protected]', SECRET2), None, IGarbage)
perform()
self.failureResultOf(d, BadCredentials)

# Have to let the substore upgrade complete
return av.store.whenFullyUpgraded()


def test_password(self):
"""
After the upgrade, the password attribute is cleared.
"""
acct = self.store.findUnique(LoginAccount)
self.assertIdentical(acct.password, None)
90 changes: 32 additions & 58 deletions axiom/test/test_userbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
from twisted.cred.portal import Portal, IRealm
from twisted.cred.checkers import ICredentialsChecker
from twisted.cred.error import UnauthorizedLogin
from twisted.cred.credentials import (
IUsernamePassword, IUsernameHashedPassword, UsernamePassword,
UsernameHashedPassword)
from twisted.cred.credentials import IUsernamePassword, UsernamePassword

from twisted.python.filepath import FilePath

Expand All @@ -32,6 +30,7 @@
from axiom.scripts import axiomatic
from axiom import errors
from axiom import dependency
from axiom.userbase import getTestContext

class IGarbage(Interface):
pass
Expand Down Expand Up @@ -180,7 +179,7 @@ def cb((interface, avatar, logout)):
ss = avatar.avatars.open()
self.assertEquals(list(userbase.getAccountNames(ss)),
[(u'alice', u'localhost')])
self.assertEquals(avatar.password, SECRET)
self.assertNotEquals(avatar.passwordHash, None)
logout()

d = self._login('alice@localhost', SECRET)
Expand Down Expand Up @@ -588,22 +587,6 @@ def test_usernamepassword(self):
return d


def test_usernameHashedPasswordDeprecated(self):
"""
Authenticating with L{twisted.cred.credentials.IUsernameHashedPassword}
credentials emits a deprecation warning.
"""
account = self.realm.addAccount(
self.localpart, self.domain, self.password)
username = u'%s@%s' % (self.localpart, self.domain)
aid = self.successResultOf(
self._requestAvatarId(
UsernameHashedPassword(username, self.password)))
self.assertEquals(aid, account.storeID)
ws = self.flushWarnings()
self.assertEquals(ws[0]['category'], DeprecationWarning)


def test_usernamepasswordInvalid(self):
"""
L{LoginSystem.requestAvatarId} fails with L{UnauthorizedLogin} if
Expand Down Expand Up @@ -637,52 +620,57 @@ def test_setPassword(self):
L{LoginAccount.setPassword} allows for logging in with the new password
and not the old.
"""
self.realm._txCryptContext, perform = getTestContext()
account = self.realm.addAccount(
self.localpart, self.domain, self.password)
username = u'%s@%s' % (self.localpart, self.domain)
self.successResultOf(account.setPassword(u'blahblah'))
self.assertEquals(
self.successResultOf(
self._requestAvatarId(
UsernamePassword(username, u'blahblah'))),
account.storeID)
self.failureResultOf(
self._requestAvatarId(
UsernamePassword(username, self.password)),
UnauthorizedLogin)
d = account.setPassword(u'blahblah')
perform()
self.successResultOf(d)
d = self._requestAvatarId(UsernamePassword(username, u'blahblah'))
perform()
self.assertEquals(self.successResultOf(d), account.storeID)
d = self._requestAvatarId(UsernamePassword(username, self.password))
perform()
self.failureResultOf(d, UnauthorizedLogin)
d = self._requestAvatarId(UsernamePassword(username, account.passwordHash))
perform()
self.failureResultOf(d, UnauthorizedLogin)


def test_replacePasswordWrong(self):
"""
L{LoginAccount.replacePassword} fails with L{BadCredentials} if an
incorrect current password is supplied.
"""
self.realm._txCryptContext, perform = getTestContext()
account = self.realm.addAccount(
self.localpart, self.domain, self.password)
self.failureResultOf(
account.replacePassword(u'blahblah', u'blah'),
errors.BadCredentials)
d = account.replacePassword(u'blahblah', u'blah')
perform()
perform()
self.failureResultOf(d, errors.BadCredentials)


def test_replacePasswordCorrect(self):
"""
L{LoginAccount.replacePassword} allows for logging in with the new
password and not the old if the correct current password is supplied.
"""
self.realm._txCryptContext, perform = getTestContext()
account = self.realm.addAccount(
self.localpart, self.domain, self.password)
username = u'%s@%s' % (self.localpart, self.domain)
self.successResultOf(
account.replacePassword(self.password, u'blahblah'))
self.assertEquals(
self.successResultOf(
self._requestAvatarId(
UsernamePassword(username, u'blahblah'))),
account.storeID)
self.failureResultOf(
self._requestAvatarId(
UsernamePassword(username, self.password)),
UnauthorizedLogin)
d = account.replacePassword(self.password, u'blahblah')
perform()
perform()
self.successResultOf(d)
d = self._requestAvatarId(UsernamePassword(username, u'blahblah'))
perform()
self.assertEquals(self.successResultOf(d), account.storeID)
d = self._requestAvatarId(UsernamePassword(username, self.password))
perform()
self.failureResultOf(d, UnauthorizedLogin)



Expand Down Expand Up @@ -712,17 +700,3 @@ def test_usernamepassword(self):
self.assertTrue(
creds.checkPassword('random string'),
"Preauthenticated did not accept an arbitrary password.")


def test_usernamehashedpassword(self):
"""
L{Preauthenticated} implements L{IUsernameHashedPassword} and succeeds
all authentication checks.
"""
creds = userbase.Preauthenticated(u'foo@bar')
self.assertTrue(
verifyObject(IUsernameHashedPassword, creds),
"Preauthenticated does not implement IUsernameHashedPassword")
self.assertTrue(
creds.checkPassword('arbitrary bytes'),
"Preauthenticated did not accept an arbitrary password.")
Loading

0 comments on commit 38dae5e

Please sign in to comment.