1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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: 7161 $"
36
37
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
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
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
66
67 return []
68 if hasattr(self.bouncer, 'getKeycardClasses'):
69
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
114 return failure
115
116 def bouncerResponse(result):
117
118
119
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
128 self.log('returning keycard for further authentication')
129 return keycard
130
131
132 self.debug('authenticated login of %r into realm %r', keycard,
133 self.realm)
134
135
136 if interfaces.IAdminMedium in ifaces:
137
138 keycard.avatarId = "admin-%06x" % self._adminCounter
139 self._adminCounter += 1
140
141 self.log(
142 'calling %r.requestAvatar(keycard=%r, mind=%r, ifaces=%r)',
143 self.realm, keycard, mind, ifaces)
144
145 return self.realm.requestAvatar(keycard.avatarId,
146 keycard, mind, *ifaces)
147
148 if hasattr(keycard, 'address'):
149 try:
150 keycard.address = mind.broker.transport.getHost().host
151 except:
152 self.debug("can't get address of remote, setting to None")
153 keycard.address = None
154
155 d = defer.maybeDeferred(self.bouncer.authenticate, keycard)
156 d.addCallback(bouncerResponse)
157 d.addErrback(onErrorCloseConnection)
158 return d
159
160 registerAdapter(_FPortalRoot, BouncerPortal, flavors.IPBRoot)
161