[Twisted-Python] exceptions.TypeError: ('Could not adapt', <twisted.spread.pb._PortalAuthChallenger instance at 0x013E3760>, <InterfaceClass twisted.cred.credentials.IUsernamePassword>)
Hi, I'm running Python 2.5 and Twisted 2.5.0 on Windows 2003 Server. I have a web site (Twisted/Nevow) where users login with a username and password. I save the username and password once they login because the same username and password are used to connect to various Perspective Broker servers, and I don't want to make users reauthenticate for every server that is connected to behind the scenes. Since the password cannot be hashed when passed to PB I store it in plaintext. Up to this point I have been using a realm and file password db as listed at the end of this email. When I recently upgraded from Twisted 2.4.0 to 2.5.0 and from Python 2.4 to 2.5 I get the following error. 2007/03/28 10:23 -0500 [Broker,0,IP_ADDRESS] Peer will receive following PB traceback: 2007/03/28 10:23 -0500 [Broker,0,IP_ADDRESS] Unhandled Error Traceback (most recent call last): File "D:\Python25\lib\site-packages\twisted\spread\pb.py", line 847, in _recvMessage netResult = object.remoteMessageReceived(self, message, netArgs, netKw) File "D:\Python25\lib\site-packages\twisted\spread\flavors.py", line 119, in remoteMessageReceived state = method(*args, **kw) File "D:\Python25\lib\site-packages\twisted\spread\pb.py", line 1217, in remote_respond d = self.portalWrapper.portal.login(self, mind, IPerspective) File "D:\Python25\lib\site-packages\twisted\cred\portal.py", line 109, in login return maybeDeferred(c.requestAvatarId, credentials --- <exception caught here> --- File "D:\Python25\lib\site-packages\twisted\internet\defer.py", line 107, in maybeDeferred result = f(*args, **kw) File "d:\python25\lib\site-packages\ratcontrol\login.py", line 195, in requestAvatarId up = credentials.IUsernamePassword(c) exceptions.TypeError: ('Could not adapt', <twisted.spread.pb._PortalAuthChallenger instance at 0x013E3760>, <InterfaceClass twisted.cred.credentials.IUsernamePassword>) 2007/03/28 10:23 -0500 [Broker,client] Unhandled error in Deferred: 2007/03/28 10:23 -0500 [Broker,client] Unhandled Error Traceback from remote host -- Traceback unavailable I looked at the Subversion log for twisted.spread.pb.py and am not sure what change is causing this failure. The contents of ratcontrol\login.py are below. Does anyone have any pointers on what I should be looking for here? Is there any way I can accomplish my goal in a more maintainable way? Thanks, Justin ---------------- from twisted.cred import portal, checkers, credentials, error from twisted.web import resource from twisted.spread import pb from twisted.python import log, components from twisted.internet.defer import maybeDeferred from twisted.internet import defer from twisted.python import failure, reflect, components from zope.interface import implements from nevow import inevow from ratcontrol.web import pages from ratcontrol import perspectives from ratcontrol import services def noLogout(): return None class RatControlRealm: """A simple implementor of cred's IRealm. For web, this gives us the LoggedIn page. For PB, this gives us a reference to the perspective. """ implements(portal.IRealm) def __init__(self): self.clients = [] self.serviceTypes = [] def addServiceType(self, serviceType): self.serviceTypes.append(serviceType) def requestAvatar(self, avatarIdAndPassword, mind, *interfaces): ## If the login is anonymous, only one item will be passed. ## If it is an authorized login, both the id and password ## will be passed. if len(avatarIdAndPassword) == 2: avatarId, avatarPassword = avatarIdAndPassword else: avatarId = avatarIdAndPassword for iface in interfaces: if iface is inevow.IResource: if avatarId is checkers.ANONYMOUS: resc = pages.NotLoggedIn() return (resource.IResource, resc, noLogout) else: resc = pages.LoggedIn(avatarId, avatarPassword, self) return resource.IResource, resc, noLogout elif iface is pb.IPerspective: if avatarId is checkers.ANONYMOUS: return (pb.IPerspective, None, None) else: log.msg("User %s logged in" % avatarId) #s = services.RatControlService() s = services.getServiceForTypes(self.serviceTypes) self.p = perspectives.IPerspectiveRatControl(s) self.p.loggedIn = 1 return (pb.IPerspective, self.p, self.p.logout) raise NotImplementedError("Can't support that interface.") class FilePasswordDB: """A file-based, text-based username/password database. Records in the datafile for this class are delimited by a particular string. The username appears in a fixed field of the columns delimited by this string, as does the password. Both fields are specifiable. If the passwords are not stored plaintext, a hash function must be supplied to convert plaintext passwords to the form stored on disk and this CredentialsChecker will only be able to check IUsernamePassword credentials. If the passwords are stored plaintext, IUsernameHashedPassword credentials will be checkable as well. """ implements(checkers.ICredentialsChecker) def __init__(self, filename, delim=':', usernameField=0, passwordField=1, caseSensitive=True, hash=None): """ @type filename: C{str} @param filename: The name of the file from which to read username and password information. @type delim: C{str} @param delim: The field delimiter used in the file. @type usernameField: C{int} @param usernameField: The index of the username after splitting a line on the delimiter. @type caseSensitive: C{bool} @param caseSensitive: If true, consider the case of the username when performing a lookup. Ignore it otherwise. @type passwordField: C{int} @param passwordField: The index of the password after splitting a line on the delimiter. @type hash: Three-argument callable. @param hash: A function used to transform the plaintext password received over the network to a format suitable for comparison against the version stored on disk. The arguments to the callable are the username, the network-supplied password, and the in-file version of the password. """ self.filename = filename self.delim = delim self.ufield = usernameField self.pfield = passwordField self.caseSensitive = caseSensitive self.hash = hash if self.hash is None: # The passwords are stored plaintext. We can support both # plaintext and hashed passwords received over the network. self.credentialInterfaces = ( credentials.IUsernamePassword, credentials.IUsernameHashedPassword ) else: # The passwords are hashed on disk. We can support only # plaintext passwords received over the network. self.credentialInterfaces = ( credentials.IUsernamePassword, ) def _cbPasswordMatch(self, matched, usernameAndPassword): ## Return both the username and password so we can save the password ## for connecting to other services once we are authenticated on one. if matched: return usernameAndPassword else: return failure.Failure(error.UnauthorizedLogin()) def getUser(self, username): try: f = file(self.filename) except: log.err() raise error.UnauthorizedLogin() else: if not self.caseSensitive: username = username.lower() for line in f: line = line.rstrip() parts = line.split(self.delim) if self.ufield >= len(parts) or self.pfield >= len(parts): continue if self.caseSensitive: if parts[self.ufield] != username: continue elif parts[self.ufield].lower() != username: continue return parts[self.ufield], parts[self.pfield] raise KeyError(username) def requestAvatarId(self, c): try: u, p = self.getUser(c.username) except KeyError: return failure.Failure(error.UnauthorizedLogin()) else: up = credentials.IUsernamePassword(c) if self.hash: if up is not None: h = self.hash(up.username, up.password, p) if h == p: return (u, p) return failure.Failure(error.UnauthorizedLogin()) else: ## Note that we pass both the username and the password return defer.maybeDeferred(c.checkPassword, p ).addCallback(self._cbPasswordMatch, (u,p))
On Wed, 28 Mar 2007 12:01:12 -0500, Justin Johnson <justinjohnson@gmail.com> wrote:
Hi,
I'm running Python 2.5 and Twisted 2.5.0 on Windows 2003 Server.
I have a web site (Twisted/Nevow) where users login with a username and password. I save the username and password once they login because the same username and password are used to connect to various Perspective Broker servers, and I don't want to make users reauthenticate for every server that is connected to behind the scenes. Since the password cannot be hashed when passed to PB I store it in plaintext.
Up to this point I have been using a realm and file password db as listed at the end of this email. When I recently upgraded from Twisted 2.4.0 to 2.5.0 and from Python 2.4 to 2.5 I get the following error.
2007/03/28 10:23 -0500 [Broker,0,IP_ADDRESS] Peer will receive following PB traceback: 2007/03/28 10:23 -0500 [Broker,0,IP_ADDRESS] Unhandled Error Traceback (most recent call last): File "D:\Python25\lib\site-packages\twisted\spread\pb.py", line 847, in _recvMessage netResult = object.remoteMessageReceived(self, message, netArgs, netKw) File "D:\Python25\lib\site-packages\twisted\spread\flavors.py", line 119, in remoteMessageReceived state = method(*args, **kw) File "D:\Python25\lib\site-packages\twisted\spread\pb.py", line 1217, in remote_respond d = self.portalWrapper.portal.login(self, mind, IPerspective) File "D:\Python25\lib\site-packages\twisted\cred\portal.py", line 109, in login return maybeDeferred(c.requestAvatarId, credentials --- <exception caught here> --- File "D:\Python25\lib\site-packages\twisted\internet\defer.py", line 107, in maybeDeferred result = f(*args, **kw) File "d:\python25\lib\site-packages\ratcontrol\login.py", line 195, in requestAvatarId up = credentials.IUsernamePassword(c) exceptions.TypeError: ('Could not adapt', <twisted.spread.pb._PortalAuthChallenger instance at 0x013E3760>,
<InterfaceClass twisted.cred.credentials.IUsernamePassword>)
2007/03/28 10:23 -0500 [Broker,client] Unhandled error in Deferred: 2007/03/28 10:23 -0500 [Broker,client] Unhandled Error Traceback from remote host -- Traceback unavailable
I looked at the Subversion log for twisted.spread.pb.py and am not sure what change is causing this failure. The contents of ratcontrol\login.py are below.
Does anyone have any pointers on what I should be looking for here? Is there any way I can accomplish my goal in a more maintainable way?
It's not clear to me how this worked in the first place. ;) The credentials object being adapted to IUsernamePassword should always have been a _PortalAuthChallenger, and so it only should have ever been adaptable to IUsernameHashedPassword and IUsernameMD5Password. The way you want to write requestAvatarId, though, is to inspect the credentials object to see what interfaces it provides and them handle it accordingly. You can do this by adapting and catching the TypeError, adapting with a default and checking for that, or using the providedBy method of the interface object. The important point is that you have to be able to handle every kind of credentials object which the you have declared you can handle (with the credentialInterfaces attribute). In this specific case, I believe your checker declared it could handle both IUsernamePassword and IUsernameHashedPassword, but its implementation can only actually handle IUsernameePassword. Hope this helps, Jean-Paul
Does anyone have any pointers on what I should be looking for here? Is there any way I can accomplish my goal in a more maintainable way?
It's not clear to me how this worked in the first place. ;) The credentials object being adapted to IUsernamePassword should always have been a _PortalAuthChallenger, and so it only should have ever been adaptable to IUsernameHashedPassword and IUsernameMD5Password.
The way you want to write requestAvatarId, though, is to inspect the credentials object to see what interfaces it provides and them handle it accordingly. You can do this by adapting and catching the TypeError, adapting with a default and checking for that, or using the providedBy method of the interface object. The important point is that you have to be able to handle every kind of credentials object which the you have declared you can handle (with the credentialInterfaces attribute). In this specific case, I believe your checker declared it could handle both IUsernamePassword and IUsernameHashedPassword, but its implementation can only actually handle IUsernameePassword.
Hope this helps,
Yes, this is helpful. It has been too long since I looked at this code. Thank you. Justin
participants (2)
-
Jean-Paul Calderone
-
Justin Johnson