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