]>
git.ipfire.org Git - ipfire.org.git/blob - src/web/base.py
11406547c62d9ae0dfdd92a90d63ef758dfff16a
14 from ..decorators
import *
17 class ratelimit(object):
19 A decorator class which limits how often a function can be called
21 def __init__(self
, *, minutes
, requests
):
22 self
.minutes
= minutes
23 self
.requests
= requests
25 def __call__(self
, method
):
26 @functools.wraps(method
)
27 async def wrapper(handler
, *args
, **kwargs
):
28 # Pass the request to the rate limiter and get a request object
29 req
= handler
.backend
.ratelimiter
.handle_request(handler
.request
,
30 handler
, minutes
=self
.minutes
, limit
=self
.requests
)
32 # If the rate limit has been reached, we won't allow
33 # processing the request and therefore send HTTP error code 429.
34 if await req
.is_ratelimited():
35 raise tornado
.web
.HTTPError(429, "Rate limit exceeded")
37 # Call the wrapped method
38 result
= method(handler
, *args
, **kwargs
)
40 # Await it if it is a coroutine
41 if asyncio
.iscoroutine(result
):
50 class BaseHandler(tornado
.web
.RequestHandler
):
52 # Mark this as private when someone is logged in
54 self
.set_header("Cache-Control", "private")
56 # Always send Vary: Cookie
57 self
.set_header("Vary", "Cookie")
59 def set_expires(self
, seconds
):
61 self
.add_header("Cache-Control", "max-age=%s, must-revalidate" % seconds
)
64 expires
= datetime
.datetime
.utcnow() + datetime
.timedelta(seconds
=seconds
)
65 self
.set_header("Expires", expires
)
67 def write_error(self
, status_code
, **kwargs
):
68 # Translate code into message
70 message
= http
.client
.responses
[status_code
]
74 self
.render("error.html", status_code
=status_code
, message
=message
, **kwargs
)
78 # Return hostname in production
79 if self
.request
.host
.endswith("ipfire.org"):
80 return self
.request
.host
82 # Remove the development prefix
83 subdomain
, delimier
, domain
= self
.request
.host
.partition(".")
85 return "%s.ipfire.org" % subdomain
87 # Return whatever it is
88 return self
.request
.host
90 def get_template_namespace(self
):
91 ns
= tornado
.web
.RequestHandler
.get_template_namespace(self
)
93 now
= datetime
.date
.today()
96 "backend" : self
.backend
,
97 "debug" : self
.application
.settings
.get("debug", False),
98 "format_size" : util
.format_size
,
99 "format_time" : util
.format_time
,
100 "hostname" : self
.hostname
,
108 def get_remote_ip(self
):
109 # Fix for clients behind a proxy that sends "X-Forwarded-For".
110 remote_ips
= self
.request
.remote_ip
.split(", ")
112 for remote_ip
in remote_ips
:
114 addr
= ipaddress
.ip_address(remote_ip
)
116 # Skip invalid IP addresses.
119 # Check if the given IP address is from a
126 # Return the last IP if nothing else worked
127 return remote_ips
.pop()
130 def current_address(self
):
131 address
= self
.get_remote_ip()
134 return util
.Address(self
.backend
, address
)
137 def current_country_code(self
):
138 if self
.current_address
:
139 return self
.current_address
.country_code
141 def get_argument_int(self
, *args
, **kwargs
):
142 arg
= self
.get_argument(*args
, **kwargs
)
144 if arg
is None or arg
== "":
150 raise tornado
.web
.HTTPError(400, "Could not convert integer: %s" % arg
)
152 def get_argument_float(self
, *args
, **kwargs
):
153 arg
= self
.get_argument(*args
, **kwargs
)
155 if arg
is None or arg
== "":
161 raise tornado
.web
.HTTPError(400, "Could not convert float: %s" % arg
)
163 def get_argument_date(self
, arg
, *args
, **kwargs
):
164 value
= self
.get_argument(arg
, *args
, **kwargs
)
169 return dateutil
.parser
.parse(value
)
171 raise tornado
.web
.HTTPError(400)
173 def get_file(self
, name
):
175 file = self
.request
.files
[name
][0]
177 return file["filename"], file["body"], file["content_type"]
183 def get_current_user(self
):
184 session_id
= self
.get_cookie("session_id")
188 # Get account from the session object
189 account
= self
.backend
.accounts
.get_by_session(session_id
, self
.request
.host
)
191 # If the account was not found or the session was not valid
192 # any more, we will remove the cookie.
194 self
.clear_cookie("session_id")
200 return self
.application
.backend
204 return self
.backend
.db
208 return self
.backend
.accounts
212 return self
.backend
.downloads
216 return self
.backend
.fireinfo
220 return self
.backend
.iuse
224 return self
.backend
.mirrors
228 return self
.backend
.netboot
232 return self
.backend
.releases
235 class APIHandler(BaseHandler
):
236 def check_xsrf_cookie(self
):
238 Do nothing here, because we cannot verify the XSRF token
243 # Do not cache any API communication
244 self
.set_header("Cache-Control", "no-cache")
247 class NotFoundHandler(BaseHandler
):
249 # Raises 404 as soon as it is called
250 raise tornado
.web
.HTTPError(404)
253 class ErrorHandler(BaseHandler
):
255 Raises any error we want
261 raise tornado
.web
.HTTPError(400)
263 raise tornado
.web
.HTTPError(code
)