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  __version__ = "$Rev: 6982 $" 
 36   
 37   
38 -class BouncerPortal(log.Loggable):
39 """ 40 I am a portal for an FPB server using a bouncer to decide on FPB client 41 access. 42 """ 43 44 logCategory = "BouncerPortal" 45
46 - def __init__(self, realm, bouncer):
47 """ 48 Create a BouncerPortal to a L{twisted.cred.portal.IRealm}. 49 50 @param realm: an implementor of L{twisted.cred.portal.IRealm} 51 @param bouncer: a bouncer to use for authentication 52 @type bouncer: L{flumotion.component.bouncers.bouncer.Bouncer} 53 """ 54 self.realm = realm 55 self.bouncer = bouncer 56 self._adminCounter = 0
57
58 - def getKeycardClasses(self):
59 """ 60 Return the Keycard interfaces supported by this portal's bouncer. 61 62 @rtype: L{twisted.internet.defer.Deferred} firing list of str 63 """ 64 if not self.bouncer: 65 # no logins will be possible, but we can wait until they try 66 # to login() to reject them 67 return [] 68 if hasattr(self.bouncer, 'getKeycardClasses'): 69 # must return a deferred 70 return self.bouncer.getKeycardClasses() 71 else: 72 interfaces = [reflect.qual(k) for k in self.bouncer.keycardClasses] 73 return defer.succeed(interfaces)
74
75 - def login(self, keycard, mind, *ifaces):
76 """ 77 Log in the keycard to the portal using the bouncer. 78 79 @param keycard: the keycard used to login 80 @type keycard: L{flumotion.common.keycards.Keycard} 81 @param mind: a reference to the client-side requester 82 @type mind: L{twisted.spread.pb.RemoteReference} 83 @param ifaces: a list of interfaces for the perspective that the 84 mind wishes to attach to 85 86 @returns: a deferred, which will fire a tuple of 87 (interface, avatarAspect, logout) or None. 88 """ 89 self.debug("_login(keycard=%r, mind=%r, ifaces=%r)" % ( 90 keycard, mind, ifaces)) 91 92 if not self.bouncer: 93 self.warning("no bouncer, refusing login") 94 mind.broker.transport.loseConnection() 95 return defer.fail(errors.NotAuthenticatedError( 96 "No bouncer configured, no logins possible")) 97 98 def onErrorCloseConnection(failure): 99 try: 100 host = mind.broker.transport.getHost() 101 remote = '%s:%d' % (host.host, host.port) 102 except: 103 remote = '(unknown)' 104 105 self.warning('failed login -- closing connection to %s', 106 remote) 107 self.debug('failure: %s', log.getFailureMessage(failure)) 108 try: 109 mind.broker.transport.loseConnection() 110 except Exception, e: 111 self.info('loseConnection failed: %s', 112 log.getExceptionMessage(e)) 113 # ignore it 114 return failure
115 116 def bouncerResponse(result): 117 # we either got a keycard as result, or None from the 118 # bouncer; would be better if the bouncers returned failures 119 # directly, but that's not how the current interface works. 120 if not result: 121 self.info("unauthorized login for interfaces %r", ifaces) 122 return defer.fail(errors.NotAuthenticatedError( 123 "Unauthorized login")) 124 125 keycard = result 126 if not keycard.state == keycards.AUTHENTICATED: 127 # challenge 128 self.log('returning keycard for further authentication') 129 return keycard 130 131 # this is where we request the Avatar and can influence naming 132 self.debug('authenticated login of %r into realm %r', keycard, 133 self.realm) 134 135 # FIXME: this is a hack 136 if interfaces.IAdminMedium in ifaces: 137 # we decide on a unique name for admin clients here 138 keycard.avatarId = "admin-%06x" % self._adminCounter 139 self._adminCounter += 1 140 141 self.log('calling %r.requestAvatar(keycard=%r, mind=%r, ifaces=%r)', 142 self.realm, keycard, mind, ifaces) 143 144 return self.realm.requestAvatar(keycard.avatarId, keycard, mind, *ifaces)
145 146 if hasattr(keycard, 'address'): 147 try: 148 keycard.address = mind.broker.transport.getHost().host 149 except: 150 self.debug("can't get address of remote, setting to None") 151 keycard.address = None 152 153 d = defer.maybeDeferred(self.bouncer.authenticate, keycard) 154 d.addCallback(bouncerResponse) 155 d.addErrback(onErrorCloseConnection) 156 return d 157 158 registerAdapter(_FPortalRoot, BouncerPortal, flavors.IPBRoot) 159