]> git.ipfire.org Git - ipfire.org.git/blame - src/web/auth.py
Drop checking URL blacklists to block users
[ipfire.org.git] / src / web / auth.py
CommitLineData
08df6527
MT
1#!/usr/bin/python
2
3import logging
4import tornado.web
5
124a8404 6from . import base
08df6527 7
170b63ba
MT
8class CacheMixin(object):
9 def prepare(self):
10 # Mark this as private when someone is logged in
11 if self.current_user:
12 self.add_header("Cache-Control", "private")
13
14 self.add_header("Vary", "Cookie")
15
16
17class AuthenticationMixin(CacheMixin):
d8a15b2e 18 def login(self, account):
08df6527 19 # User has logged in, create a session
906e1e6a
MT
20 session_id, session_expires = self.backend.accounts.create_session(
21 account, self.request.host)
08df6527
MT
22
23 # Check if a new session was created
24 if not session_id:
25 raise tornado.web.HTTPError(500, "Could not create session")
26
27 # Send session cookie to the client
28 self.set_cookie("session_id", session_id,
29 domain=self.request.host, expires=session_expires)
30
31 def logout(self):
32 session_id = self.get_cookie("session_id")
33 if not session_id:
34 return
35
906e1e6a 36 success = self.backend.accounts.destroy_session(session_id, self.request.host)
08df6527
MT
37 if success:
38 self.clear_cookie("session_id")
39
40
08df6527
MT
41class LoginHandler(AuthenticationMixin, base.BaseHandler):
42 def get(self):
43 next = self.get_argument("next", None)
44
a76ac21e
MT
45 self.render("auth/login.html", next=next,
46 incorrect=False, username=None)
08df6527 47
372ef119 48 @base.ratelimit(minutes=60, requests=5)
08df6527
MT
49 def post(self):
50 username = self.get_argument("username")
51 password = self.get_argument("password")
a76ac21e 52 next = self.get_argument("next", "/")
08df6527 53
328a7710
MT
54 # Find user
55 account = self.backend.accounts.auth(username, password)
56 if not account:
a76ac21e
MT
57 logging.error("Unknown user or invalid password: %s" % username)
58
59 # Set status to 401
60 self.set_status(401)
61
62 # Render login page again
63 return self.render("auth/login.html",
64 incorrect=True, username=username, next=next,
65 )
328a7710
MT
66
67 # Create session
08df6527 68 with self.db.transaction():
328a7710 69 self.login(account)
08df6527 70
a76ac21e
MT
71 # Redirect the user
72 return self.redirect(next)
08df6527
MT
73
74
75class LogoutHandler(AuthenticationMixin, base.BaseHandler):
76 def get(self):
77 with self.db.transaction():
78 self.logout()
79
80 # Get back to the start page
81 self.redirect("/")
9b8ff27d
MT
82
83
f32dd17f 84class RegisterHandler(base.BaseHandler):
f32dd17f 85 def get(self):
d521c9df
MT
86 # Redirect logged in users away
87 if self.current_user:
88 self.redirect("/")
89
f32dd17f
MT
90 self.render("auth/register.html")
91
372ef119 92 @base.ratelimit(minutes=24*60, requests=5)
9fdf4fb7 93 async def post(self):
f32dd17f
MT
94 uid = self.get_argument("uid")
95 email = self.get_argument("email")
96
97 first_name = self.get_argument("first_name")
98 last_name = self.get_argument("last_name")
99
23f84bbc 100 # Check if this is a spam account
9fdf4fb7 101 is_spam = await self.backend.accounts.check_spam(uid, email,
23f84bbc
MT
102 address=self.get_remote_ip())
103
104 if is_spam:
105 self.render("auth/register-spam.html")
106 return
107
f32dd17f 108 # Register account
f2ba8a1f
MT
109 try:
110 with self.db.transaction():
111 self.backend.accounts.register(uid, email,
757372cd
MT
112 first_name=first_name, last_name=last_name,
113 country_code=self.current_country_code)
f2ba8a1f 114 except ValueError as e:
09c67399 115 raise tornado.web.HTTPError(400, "%s" % e) from e
f32dd17f
MT
116
117 self.render("auth/register-success.html")
118
119
d8a15b2e
MT
120class ActivateHandler(AuthenticationMixin, base.BaseHandler):
121 def get(self, uid, activation_code):
b4d72c76 122 self.render("auth/activate.html")
d8a15b2e
MT
123
124 def post(self, uid, activation_code):
b4d72c76
MT
125 password1 = self.get_argument("password1")
126 password2 = self.get_argument("password2")
d8a15b2e 127
b4d72c76
MT
128 if not password1 == password2:
129 raise tornado.web.HTTPError(400, "Passwords do not match")
d8a15b2e 130
b4d72c76
MT
131 with self.db.transaction():
132 account = self.backend.accounts.activate(uid, activation_code)
133 if not account:
134 raise tornado.web.HTTPError(400, "Account not found: %s" % uid)
d8a15b2e 135
b4d72c76
MT
136 # Set the new password
137 account.passwd(password1)
d8a15b2e 138
b4d72c76
MT
139 # Create session
140 self.login(account)
d8a15b2e 141
b00cc400
MT
142 # Redirect to success page
143 self.render("auth/activated.html", account=account)
689effd0
MT
144
145
391ede9e 146class PasswordResetInitiationHandler(base.BaseHandler):
c7594d58
MT
147 def get(self):
148 username = self.get_argument("username", None)
149
150 self.render("auth/password-reset-initiation.html", username=username)
151
152 @base.ratelimit(minutes=60, requests=5)
153 def post(self):
154 username = self.get_argument("username")
155
156 # Fetch account and submit password reset
157 account = self.backend.accounts.get_by_uid(username)
158 if account:
159 with self.db.transaction():
391ede9e 160 account.request_password_reset()
c7594d58
MT
161
162 self.render("auth/password-reset-successful.html")
163
164
391ede9e
MT
165class PasswordResetHandler(AuthenticationMixin, base.BaseHandler):
166 def get(self, uid, reset_code):
167 account = self.backend.accounts.get_by_uid(uid)
168 if not account:
169 raise tornado.web.HTTPError(404, "Could not find account: %s" % uid)
170
171 self.render("auth/password-reset.html", account=account)
172
173 def post(self, uid, reset_code):
174 account = self.backend.accounts.get_by_uid(uid)
175 if not account:
176 raise tornado.web.HTTPError(404, "Could not find account: %s" % uid)
177
178 password1 = self.get_argument("password1")
179 password2 = self.get_argument("password2")
180
181 if not password1 == password2:
182 raise tornado.web.HTTPError(400, "Passwords do not match")
183
184 # Try to perform password reset
185 with self.db.transaction():
186 account.reset_password(reset_code, password1)
187
188 # Login the user straight away after reset was successful
189 self.login(account)
190
191 # Redirect back to /
192 self.redirect("/")
193
194
689effd0
MT
195class APICheckUID(base.APIHandler):
196 @base.ratelimit(minutes=10, requests=100)
197 def get(self):
198 uid = self.get_argument("uid")
199 result = None
200
201 if not uid:
202 result = "empty"
203
204 # Check if the username is syntactically valid
205 elif not self.backend.accounts.uid_is_valid(uid):
206 result = "invalid"
207
208 # Check if the username is already taken
209 elif self.backend.accounts.uid_exists(uid):
210 result = "taken"
211
212 # Username seems to be okay
213 self.finish({ "result" : result or "ok" })