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