]> git.ipfire.org Git - ipfire.org.git/blob - src/web/base.py
talk: Drop old FreeSWITCH integration
[ipfire.org.git] / src / web / base.py
1 #!/usr/bin/python
2
3 import datetime
4 import dateutil.parser
5 import functools
6 import http.client
7 import ipaddress
8 import logging
9 import time
10 import tornado.locale
11 import tornado.web
12
13 from ..decorators import *
14 from .. import util
15
16 class ratelimit(object):
17 def __init__(self, minutes=15, requests=180):
18 self.minutes = minutes
19 self.requests = requests
20
21 def __call__(self, method):
22 @functools.wraps(method)
23 def wrapper(handler, *args, **kwargs):
24 # Pass the request to the rate limiter and get a request object
25 req = handler.backend.ratelimiter.handle_request(handler.request,
26 handler, minutes=self.minutes, limit=self.requests)
27
28 # If the rate limit has been reached, we won't allow
29 # processing the request and therefore send HTTP error code 429.
30 if req.is_ratelimited():
31 raise tornado.web.HTTPError(429, "Rate limit exceeded")
32
33 return method(handler, *args, **kwargs)
34
35 return wrapper
36
37
38 class BaseHandler(tornado.web.RequestHandler):
39 def prepare(self):
40 # Mark this as private when someone is logged in
41 if self.current_user:
42 self.set_header("Cache-Control", "private")
43
44 # Always send Vary: Cookie
45 self.set_header("Vary", "Cookie")
46
47 def set_expires(self, seconds):
48 # For HTTP/1.1
49 self.add_header("Cache-Control", "max-age=%s, must-revalidate" % seconds)
50
51 # For HTTP/1.0
52 expires = datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds)
53 self.set_header("Expires", expires)
54
55 def write_error(self, status_code, **kwargs):
56 # Translate code into message
57 try:
58 message = http.client.responses[status_code]
59 except KeyError:
60 message = None
61
62 self.render("error.html", status_code=status_code, message=message, **kwargs)
63
64 @property
65 def hostname(self):
66 # Return hostname in production
67 if self.request.host.endswith("ipfire.org"):
68 return self.request.host
69
70 # Remove the development prefix
71 subdomain, delimier, domain = self.request.host.partition(".")
72 if subdomain:
73 return "%s.ipfire.org" % subdomain
74
75 # Return whatever it is
76 return self.request.host
77
78 def get_template_namespace(self):
79 ns = tornado.web.RequestHandler.get_template_namespace(self)
80
81 now = datetime.date.today()
82
83 ns.update({
84 "backend" : self.backend,
85 "debug" : self.application.settings.get("debug", False),
86 "format_size" : util.format_size,
87 "format_time" : util.format_time,
88 "hostname" : self.hostname,
89 "now" : now,
90 "year" : now.year,
91 })
92
93 return ns
94
95 def get_remote_ip(self):
96 # Fix for clients behind a proxy that sends "X-Forwarded-For".
97 remote_ips = self.request.remote_ip.split(", ")
98
99 for remote_ip in remote_ips:
100 try:
101 addr = ipaddress.ip_address(remote_ip)
102 except ValueError:
103 # Skip invalid IP addresses.
104 continue
105
106 # Check if the given IP address is from a
107 # private network.
108 if addr.is_private:
109 continue
110
111 return remote_ip
112
113 # Return the last IP if nothing else worked
114 return remote_ips.pop()
115
116 @lazy_property
117 def current_address(self):
118 address = self.get_remote_ip()
119
120 if address:
121 return util.Address(self.backend, address)
122
123 @lazy_property
124 def current_country_code(self):
125 if self.current_address:
126 return self.current_address.country_code
127
128 def get_argument_int(self, *args, **kwargs):
129 arg = self.get_argument(*args, **kwargs)
130
131 if arg is None or arg == "":
132 return
133
134 try:
135 return int(arg)
136 except ValueError:
137 raise tornado.web.HTTPError(400, "Could not convert integer: %s" % arg)
138
139 def get_argument_float(self, *args, **kwargs):
140 arg = self.get_argument(*args, **kwargs)
141
142 if arg is None or arg == "":
143 return
144
145 try:
146 return float(arg)
147 except ValueError:
148 raise tornado.web.HTTPError(400, "Could not convert float: %s" % arg)
149
150 def get_argument_date(self, arg, *args, **kwargs):
151 value = self.get_argument(arg, *args, **kwargs)
152 if value is None:
153 return
154
155 try:
156 return dateutil.parser.parse(value)
157 except ValueError:
158 raise tornado.web.HTTPError(400)
159
160 def get_file(self, name):
161 try:
162 file = self.request.files[name][0]
163
164 return file["filename"], file["body"], file["content_type"]
165 except KeyError:
166 return None
167
168 # Login stuff
169
170 def get_current_user(self):
171 session_id = self.get_cookie("session_id")
172 if not session_id:
173 return
174
175 # Get account from the session object
176 account = self.backend.accounts.get_by_session(session_id, self.request.host)
177
178 # If the account was not found or the session was not valid
179 # any more, we will remove the cookie.
180 if not account:
181 self.clear_cookie("session_id")
182
183 return account
184
185 @property
186 def backend(self):
187 return self.application.backend
188
189 @property
190 def db(self):
191 return self.backend.db
192
193 @property
194 def accounts(self):
195 return self.backend.accounts
196
197 @property
198 def downloads(self):
199 return self.backend.downloads
200
201 @property
202 def fireinfo(self):
203 return self.backend.fireinfo
204
205 @property
206 def iuse(self):
207 return self.backend.iuse
208
209 @property
210 def memcached(self):
211 return self.backend.memcache
212
213 @property
214 def mirrors(self):
215 return self.backend.mirrors
216
217 @property
218 def netboot(self):
219 return self.backend.netboot
220
221 @property
222 def releases(self):
223 return self.backend.releases
224
225
226 class APIHandler(BaseHandler):
227 def check_xsrf_cookie(self):
228 """
229 Do nothing here, because we cannot verify the XSRF token
230 """
231 pass
232
233
234 class NotFoundHandler(BaseHandler):
235 def prepare(self):
236 # Raises 404 as soon as it is called
237 raise tornado.web.HTTPError(404)
238
239
240 class ErrorHandler(BaseHandler):
241 """
242 Raises any error we want
243 """
244 def get(self, code):
245 try:
246 code = int(code)
247 except:
248 raise tornado.web.HTTPError(400)
249
250 raise tornado.web.HTTPError(code)