[Twisted-web] Sessions and Authentication for Web2

Phil Mayers p.mayers at imperial.ac.uk
Sat Nov 19 16:00:31 MST 2005


David Reid wrote:
> 
> But in Basic and Digest auth you don't have the username until you get
> the response to your challenge.  So this is where IAuthorizer comes in
> it handles all the steps prior to having something that you can use to
> build a credentials.  

I'm no cred expert, and I dislike it conceptually, but as far as I can 
tell it's got all the facilities it needs. HTTP is slightly more complex 
so I'll start with a SASL-ised imap-like example, to see if I've got the 
right idea:

class simpleproto(basic.LineReceiver):
   def lineReceived(self, line):
     if self.creds:
       # forward to CR module that's in progress
       self.creds.lineReceived(line)
     else:
       cmd, rest = line.split(' ', 1)
       fn = getattr(self, 'do_'+cmd, None)
       if not fn:
         self.transport.write('NO unknown command %s\n' % (cmd,))
         self.transport.loseConnection()

   def do_AUTHENTICATE(self, mechanism):
     if mechanism=='GSSAPI':
       self.creds = ChallengeResponse(self)
       return self.portal.login(self.creds).addCallbacks(self.authok, 
self.authfail)

   def authok(self, avatar):
     self.avatar = avatar
     self.lineReceived = avatar.lineReceived

   def authfail(self, why):
     self.transport.write(why.getErrorMessage()+'\n')
     self.transport.loseConnection()

class ChallengeResponse:
   def __init__(self, conn):
     self.conn = conn
     self.started = False
     self.context = hypotheticalGssapiModule.init()

   def lineReceived(self, line):
     done, response = self.context.input(line.decode('base64'))
     self.conn.transport.write(response.encode('base64')+'\n')
     if done:
       if self.context.success():
         self.finish.callback(self.context.getPrincipal())
       else:
         self.finish.errback(self.context.errMsg())

class GssapiChecker:
   implements(checkers.ICredentialsChecker)
   def requestAvatarId(self, creds):
     creds.finish = defer.Deferred()
     return creds.finish

The neat thing about cred is that the code above (if it worked at all, 
which it would with only slight fiddling) doesn't require the portal or 
realm to change. Similarly, only a bit of fiddling permits multi-method 
auth e.g. for IMAP brokenness:

def do_LOGIN(self, data):
   user, pass = data.split(' ', 1)
   creds = credentials.UsernamePassword(user, pass)
   return self.portal.login(creds).addCallbacks(...)

...and add an appropriate checker to the portal.

HTTP is a bit of a pain because of the "connectionless" basis. The RFCs 
for the hacky mechanisms like "Negotiate" (the MS-ism for kerberos over 
HTTP) and such show that. Digest would want some kind of stateless 
version - you're effectively authenticating requests as opposed to the 
connection, but the basic principle is the same. I'm sure you know all this.

So am I missing something? It looks like cred needs no extending?



More information about the Twisted-web mailing list