]> git.ipfire.org Git - ipfire.org.git/blame - src/web/base.py
Remove unused blacklist feature
[ipfire.org.git] / src / web / base.py
CommitLineData
940227cb
MT
1#!/usr/bin/python
2
cc3b928d 3import datetime
66862195 4import dateutil.parser
cfe7d74c 5import functools
11347e46 6import http.client
a69e87a1 7import ipaddress
47ed77ed 8import logging
940227cb
MT
9import time
10import tornado.locale
11import tornado.web
12
f110a9ff 13from ..decorators import *
a95c2f97 14from .. import util
60024cc8 15
372ef119
MT
16class 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
cfe7d74c 37
940227cb 38class BaseHandler(tornado.web.RequestHandler):
f6ed3d4d
MT
39 def set_expires(self, seconds):
40 # For HTTP/1.1
b592d7c8 41 self.add_header("Cache-Control", "max-age=%s, must-revalidate" % seconds)
f6ed3d4d
MT
42
43 # For HTTP/1.0
44 expires = datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds)
b592d7c8 45 self.add_header("Expires", expires)
f6ed3d4d 46
37ed7c3c
MT
47 def write_error(self, status_code, **kwargs):
48 # Translate code into message
49 try:
11347e46 50 message = http.client.responses[status_code]
37ed7c3c
MT
51 except KeyError:
52 message = None
53
54 self.render("error.html", status_code=status_code, message=message, **kwargs)
55
d3d02231
MT
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
4ca1a601
MT
62 @property
63 def hostname(self):
64 # Remove the development prefix
65 return self.request.host.replace(".dev.", ".")
66
bff08cb0
MT
67 def get_template_namespace(self):
68 ns = tornado.web.RequestHandler.get_template_namespace(self)
69
911064cf 70 now = datetime.date.today()
cc3b928d 71
bff08cb0 72 ns.update({
6c13ca2d 73 "backend" : self.backend,
5613b94b 74 "debug" : self.application.settings.get("debug", False),
a95c2f97
MT
75 "format_size" : util.format_size,
76 "format_time" : util.format_time,
4ca1a601 77 "hostname" : self.hostname,
911064cf
MT
78 "now" : now,
79 "year" : now.year,
bff08cb0 80 })
940227cb 81
bff08cb0 82 return ns
940227cb 83
9068dba1
MT
84 def get_remote_ip(self):
85 # Fix for clients behind a proxy that sends "X-Forwarded-For".
66862195 86 remote_ips = self.request.remote_ip.split(", ")
494d80e6 87
66862195
MT
88 for remote_ip in remote_ips:
89 try:
a69e87a1 90 addr = ipaddress.ip_address(remote_ip)
66862195
MT
91 except ValueError:
92 # Skip invalid IP addresses.
93 continue
9068dba1 94
66862195
MT
95 # Check if the given IP address is from a
96 # private network.
97 if addr.is_private:
98 continue
9068dba1 99
66862195 100 return remote_ip
9068dba1 101
494d80e6
MT
102 # Return the last IP if nothing else worked
103 return remote_ips.pop()
104
cfe7d74c 105 @lazy_property
440aba92 106 def current_address(self):
cfe7d74c
MT
107 address = self.get_remote_ip()
108
109 if address:
440aba92 110 return util.Address(self.backend, address)
cfe7d74c 111
f110a9ff
MT
112 @lazy_property
113 def current_country_code(self):
440aba92
MT
114 if self.current_address:
115 return self.current_address.country_code
9068dba1 116
bd2723d4
MT
117 def get_argument_int(self, *args, **kwargs):
118 arg = self.get_argument(*args, **kwargs)
119
120 if arg is None or arg == "":
121 return
122
123 try:
124 return int(arg)
125 except ValueError:
a5f94966
MT
126 raise tornado.web.HTTPError(400, "Could not convert integer: %s" % arg)
127
128 def get_argument_float(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 float(arg)
136 except ValueError:
137 raise tornado.web.HTTPError(400, "Could not convert float: %s" % arg)
bd2723d4 138
66862195
MT
139 def get_argument_date(self, arg, *args, **kwargs):
140 value = self.get_argument(arg, *args, **kwargs)
141 if value is None:
142 return
143
144 try:
145 return dateutil.parser.parse(value)
146 except ValueError:
147 raise tornado.web.HTTPError(400)
148
5cc10421
MT
149 def get_file(self, name):
150 try:
151 file = self.request.files[name][0]
152
153 return file["filename"], file["body"], file["content_type"]
154 except KeyError:
155 return None
156
66862195
MT
157 # Login stuff
158
159 def get_current_user(self):
160 session_id = self.get_cookie("session_id")
161 if not session_id:
162 return
163
164 # Get account from the session object
165 account = self.backend.accounts.get_by_session(session_id, self.request.host)
166
167 # If the account was not found or the session was not valid
168 # any more, we will remove the cookie.
169 if not account:
170 self.clear_cookie("session_id")
171
172 return account
173
a6dc0bad
MT
174 @property
175 def backend(self):
176 return self.application.backend
177
9068dba1
MT
178 @property
179 def db(self):
180 return self.backend.db
181
940227cb
MT
182 @property
183 def accounts(self):
a6dc0bad 184 return self.backend.accounts
940227cb
MT
185
186 @property
9068dba1
MT
187 def downloads(self):
188 return self.backend.downloads
189
66862195
MT
190 @property
191 def fireinfo(self):
192 return self.backend.fireinfo
193
9068dba1
MT
194 @property
195 def iuse(self):
196 return self.backend.iuse
940227cb 197
e28b082e
MT
198 @property
199 def memcached(self):
200 return self.backend.memcache
201
940227cb
MT
202 @property
203 def mirrors(self):
9068dba1
MT
204 return self.backend.mirrors
205
206 @property
207 def netboot(self):
208 return self.backend.netboot
940227cb 209
940227cb
MT
210 @property
211 def releases(self):
9068dba1 212 return self.backend.releases
940227cb 213
940227cb 214 @property
66862195
MT
215 def talk(self):
216 return self.backend.talk
940227cb 217
66862195 218
689effd0
MT
219class APIHandler(BaseHandler):
220 def check_xsrf_cookie(self):
221 """
222 Do nothing here, because we cannot verify the XSRF token
223 """
224 pass
225
226
3403dc5e
MT
227class NotFoundHandler(BaseHandler):
228 def prepare(self):
229 # Raises 404 as soon as it is called
230 raise tornado.web.HTTPError(404)
231
3403dc5e 232
37ed7c3c
MT
233class ErrorHandler(BaseHandler):
234 """
235 Raises any error we want
236 """
237 def get(self, code):
238 try:
239 code = int(code)
240 except:
241 raise tornado.web.HTTPError(400)
242
243 raise tornado.web.HTTPError(code)