1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import struct
23 import socket
24
25 from twisted.web import http, server
26 from twisted.web import resource as web_resource
27 from twisted.internet import reactor, defer
28 from twisted.python import reflect
29
30 from flumotion.configure import configure
31 from flumotion.common import errors
32
33 from flumotion.common import common, log, keycards
34
35
36
37 HTTP_SERVER_NAME = 'FlumotionHTTPServer'
38 HTTP_SERVER_VERSION = configure.version
39
40 ERROR_TEMPLATE = """<!doctype html public "-//IETF//DTD HTML 2.0//EN">
41 <html>
42 <head>
43 <title>%(code)d %(error)s</title>
44 </head>
45 <body>
46 <h2>%(code)d %(error)s</h2>
47 </body>
48 </html>
49 """
50
51 HTTP_SERVER = '%s/%s' % (HTTP_SERVER_NAME, HTTP_SERVER_VERSION)
52
53
54
55
57 """
58 I am a base class for all Issuers.
59 An issuer issues keycards of a given class based on an object
60 (incoming HTTP request, ...)
61 """
62 - def issue(self, *args, **kwargs):
63 """
64 Return a keycard, or None, based on the given arguments.
65 """
66 raise NotImplementedError
67
69 """
70 I create L{flumotion.common.keycards.Keycard} based on just a
71 standard HTTP request. Useful for authenticating based on
72 server-side checks such as time, rather than client credentials.
73 """
74 - def issue(self, request):
78
80 """
81 I create L{flumotion.common.keycards.KeycardUACPP} keycards based on
82 an incoming L{twisted.protocols.http.Request} request's standard
83 HTTP authentication information.
84 """
85 - def issue(self, request):
94
96 """
97 I create L{flumotion.common.keycards.KeycardToken} keycards based on
98 an incoming L{twisted.protocols.http.Request} request's GET "token"
99 parameter.
100 """
101 - def issue(self, request):
113
115 """
116 Mixin for handling HTTP authentication for twisted.web Resources, using
117 issuers and bouncers.
118 """
119
120 logCategory = 'httpauth'
121
122 __reserve_fds__ = 50
123
125
126 self._fdToKeycard = {}
127 self._idToKeycard = {}
128 self._fdToDurationCall = {}
129 self._domain = None
130 self._issuer = HTTPAuthIssuer()
131 self.bouncerName = None
132 self.requesterId = component.getName()
133 self._defaultDuration = None
134
135
136 - def setDomain(self, domain):
137 """
138 Set a domain name on the resource, used in HTTP auth challenges and
139 on the keycard.
140
141 @type domain: string
142 """
143 self._domain = domain
144
146 self.bouncerName = bouncerName
147
149 self.requesterId = requesterId
150
152 self._defaultDuration = defaultDuration
153
155
156
157 if issuerClass == 'HTTPTokenIssuer':
158 self._issuer = HTTPTokenIssuer()
159 elif issuerClass == 'HTTPAuthIssuer':
160 self._issuer = HTTPAuthIssuer()
161 elif issuerClass == 'HTTPGenericIssuer':
162 self._issuer = HTTPGenericIssuer()
163 else:
164 raise ValueError, "issuerClass %s not accepted" % issuerClass
165
186
187
190
193
196
198 if self.bouncerName and self._fdToKeycard.has_key(fd):
199 keycard = self._fdToKeycard[fd]
200 del self._fdToKeycard[fd]
201 del self._idToKeycard[keycard.id]
202 self.debug('[fd %5d] asking bouncer %s to remove keycard id %s' % (
203 fd, self.bouncerName, keycard.id))
204 self.cleanupKeycard(self.bouncerName, keycard)
205 if self._fdToDurationCall.has_key(fd):
206 self.debug('[fd %5d] canceling later expiration call' % fd)
207 self._fdToDurationCall[fd].cancel()
208 del self._fdToDurationCall[fd]
209
211 """
212 Expire a client due to a duration expiration.
213 """
214 self.debug('[fd %5d] duration exceeded, expiring client' % fd)
215
216
217 if self._fdToDurationCall.has_key(fd):
218 del self._fdToDurationCall[fd]
219
220 self.debug('[fd %5d] asking streamer to remove client' % fd)
221 self.clientDone(fd)
222
224 """
225 Expire a client's connection associated with the keycard Id.
226 """
227 keycard = self._idToKeycard[keycardId]
228 fd = keycard._fd
229
230 self.debug('[fd %5d] expiring client' % fd)
231
232 if self._fdToDurationCall.has_key(fd):
233 self.debug('[fd %5d] canceling later expiration call' % fd)
234 self._fdToDurationCall[fd].cancel()
235 del self._fdToDurationCall[fd]
236
237 self.debug('[fd %5d] asking streamer to remove client' % fd)
238 self.clientDone(fd)
239
240
241
248
273
278
296
300
302 """
303 Add an IP filter of the form IP/prefix-length (CIDR syntax), or just
304 a single IP address
305 """
306 definition = filter.split('/')
307 if len(definition) == 2:
308 (net, prefixlen) = definition
309 prefixlen = int(prefixlen)
310 elif len(definition) == 1:
311 net = definition[0]
312 prefixlen = 32
313 else:
314 raise errors.ConfigError(
315 "Cannot parse filter definition %s" % filter)
316
317 if prefixlen < 0 or prefixlen > 32:
318 raise errors.ConfigError("Invalid prefix length")
319
320 mask = ~((1 << (32 - prefixlen)) - 1)
321 try:
322 net = struct.unpack(">I", socket.inet_pton(socket.AF_INET, net))[0]
323 except:
324 raise errors.ConfigError("Failed to parse network address %s" % net)
325 net = net & mask
326
327 self.filters.append((net, mask))
328
330 """
331 Return true if ip is in any of the defined network(s) for this filter
332 """
333
334 realip = struct.unpack(">I", socket.inet_pton(socket.AF_INET, ip))[0]
335 for f in self.filters:
336 if (realip & f[1]) == f[0]:
337 return True
338 return False
339