Package flumotion :: Package twisted :: Module portal
[hide private]

Source Code for Module flumotion.twisted.portal

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """ 
 23  portal-related functionality inspired by twisted.cred.portal 
 24  """ 
 25   
 26  from twisted.spread import flavors 
 27  from twisted.internet import defer 
 28  from twisted.cred.portal import Portal 
 29  from twisted.python import failure, reflect 
 30  from twisted.python.components import registerAdapter 
 31   
 32  from flumotion.common import keycards, log, interfaces, errors 
 33  from flumotion.twisted.pb import _FPortalRoot 
 34   
35 -class BouncerPortal(log.Loggable):
36 """ 37 I am a portal for an FPB server using a bouncer to decide on FPB client 38 access. 39 """ 40 41 logCategory = "BouncerPortal" 42
43 - def __init__(self, realm, bouncer):
44 """ 45 Create a BouncerPortal to a L{twisted.cred.portal.IRealm}. 46 47 @param realm: an implementor of L{twisted.cred.portal.IRealm} 48 @param bouncer: a bouncer to use for authentication 49 @type bouncer: L{flumotion.component.bouncers.bouncer.Bouncer} 50 """ 51 self.realm = realm 52 self.bouncer = bouncer 53 self._adminCounter = 0
54
55 - def getKeycardClasses(self):
56 """ 57 Return the Keycard interfaces supported by this portal's bouncer. 58 59 @rtype: L{twisted.internet.defer.Deferred} firing list of str 60 """ 61 if not self.bouncer: 62 # no logins will be possible, but we can wait until they try 63 # to login() to reject them 64 return [] 65 if hasattr(self.bouncer, 'getKeycardClasses'): 66 # must return a deferred 67 return self.bouncer.getKeycardClasses() 68 else: 69 list = [reflect.qual(k) for k in self.bouncer.keycardClasses] 70 return defer.succeed(list)
71
72 - def login(self, keycard, mind, *ifaces):
73 """ 74 Log in the keycard to the portal using the bouncer. 75 76 @param keycard: the keycard used to login 77 @type keycard: L{flumotion.common.keycards.Keycard} 78 @param mind: a reference to the client-side requester 79 @type mind: L{twisted.spread.pb.RemoteReference} 80 @param ifaces: a list of interfaces for the perspective that the 81 mind wishes to attach to 82 83 @returns: a deferred, which will fire a tuple of 84 (interface, avatarAspect, logout) or None. 85 """ 86 self.debug("_login(keycard=%r, mind=%r, ifaces=%r)" % ( 87 keycard, mind, ifaces)) 88 89 if not self.bouncer: 90 self.warning("no bouncer, refusing login") 91 mind.broker.transport.loseConnection() 92 return defer.fail(errors.NotAuthenticatedError( 93 "No bouncer configured, no logins possible")) 94 95 def onErrorCloseConnection(failure): 96 try: 97 host = mind.broker.transport.getHost() 98 remote = '%s:%d' % (host.host, host.port) 99 except: 100 remote = '(unknown)' 101 102 self.warning('failed login -- closing connection to %s', 103 remote) 104 self.debug('failure: %s', log.getFailureMessage(failure)) 105 try: 106 mind.broker.transport.loseConnection() 107 except Exception, e: 108 self.info('loseConnection failed: %s', 109 log.getExceptionMessage(e)) 110 # ignore it 111 return failure
112 113 def bouncerResponse(result): 114 # we either got a keycard as result, or None from the 115 # bouncer; would be better if the bouncers returned failures 116 # directly, but that's not how the current interface works. 117 if not result: 118 self.info("unauthorized login for interfaces %r", ifaces) 119 return defer.fail(errors.NotAuthenticatedError( 120 "Unauthorized login")) 121 122 keycard = result 123 if not keycard.state == keycards.AUTHENTICATED: 124 # challenge 125 self.log('returning keycard for further authentication') 126 return keycard 127 128 # this is where we request the Avatar and can influence naming 129 self.debug('authenticated login of %r into realm %r', keycard, 130 self.realm) 131 132 # FIXME: this is a hack 133 if interfaces.IAdminMedium in ifaces: 134 # we decide on a unique name for admin clients here 135 keycard.avatarId = "admin-%06x" % self._adminCounter 136 self._adminCounter += 1 137 138 self.log('calling %r.requestAvatar(keycard=%r, mind=%r, ifaces=%r)', 139 self.realm, keycard, mind, ifaces) 140 141 return self.realm.requestAvatar(keycard.avatarId, keycard, mind, *ifaces)
142 143 if hasattr(keycard, 'address'): 144 try: 145 keycard.address = mind.broker.transport.getHost().host 146 except: 147 self.debug("can't get address of remote, setting to None") 148 keycard.address = None 149 150 d = defer.maybeDeferred(self.bouncer.authenticate, keycard) 151 d.addCallback(bouncerResponse) 152 d.addErrback(onErrorCloseConnection) 153 return d 154 155 registerAdapter(_FPortalRoot, BouncerPortal, flavors.IPBRoot) 156