]> git.ipfire.org Git - ipfire.org.git/blame - src/web/auth.py
cookie: Set secure attribute
[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
6d66cec6 14 self.add_header("Cache-Control", "no-store")
170b63ba
MT
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,
abd1d7aa 29 domain=self.request.host, expires=session_expires, secure=True)
08df6527
MT
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
53a15fe0 48 @base.ratelimit(minutes=15, requests=10)
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
6ff1f2fe 84class RegisterHandler(CacheMixin, base.BaseHandler):
f32dd17f 85 def get(self):
d521c9df
MT
86 # Redirect logged in users away
87 if self.current_user:
88 self.redirect("/")
73f2f29a 89 return
d521c9df 90
f32dd17f
MT
91 self.render("auth/register.html")
92
53a15fe0 93 @base.ratelimit(minutes=15, requests=5)
9fdf4fb7 94 async def post(self):
f32dd17f
MT
95 uid = self.get_argument("uid")
96 email = self.get_argument("email")
97
98 first_name = self.get_argument("first_name")
99 last_name = self.get_argument("last_name")
100
23f84bbc 101 # Check if this is a spam account
a5343559 102 is_spam = await self.backend.accounts.check_spam(email,
23f84bbc
MT
103 address=self.get_remote_ip())
104
105 if is_spam:
106 self.render("auth/register-spam.html")
107 return
108
f32dd17f 109 # Register account
f2ba8a1f
MT
110 try:
111 with self.db.transaction():
112 self.backend.accounts.register(uid, email,
757372cd
MT
113 first_name=first_name, last_name=last_name,
114 country_code=self.current_country_code)
f2ba8a1f 115 except ValueError as e:
09c67399 116 raise tornado.web.HTTPError(400, "%s" % e) from e
f32dd17f
MT
117
118 self.render("auth/register-success.html")
119
120
d8a15b2e
MT
121class ActivateHandler(AuthenticationMixin, base.BaseHandler):
122 def get(self, uid, activation_code):
b4d72c76 123 self.render("auth/activate.html")
d8a15b2e
MT
124
125 def post(self, uid, activation_code):
b4d72c76
MT
126 password1 = self.get_argument("password1")
127 password2 = self.get_argument("password2")
d8a15b2e 128
b4d72c76
MT
129 if not password1 == password2:
130 raise tornado.web.HTTPError(400, "Passwords do not match")
d8a15b2e 131
b4d72c76
MT
132 with self.db.transaction():
133 account = self.backend.accounts.activate(uid, activation_code)
134 if not account:
135 raise tornado.web.HTTPError(400, "Account not found: %s" % uid)
d8a15b2e 136
b4d72c76
MT
137 # Set the new password
138 account.passwd(password1)
d8a15b2e 139
b4d72c76
MT
140 # Create session
141 self.login(account)
d8a15b2e 142
b00cc400
MT
143 # Redirect to success page
144 self.render("auth/activated.html", account=account)
689effd0
MT
145
146
6ff1f2fe 147class PasswordResetInitiationHandler(CacheMixin, base.BaseHandler):
c7594d58
MT
148 def get(self):
149 username = self.get_argument("username", None)
150
151 self.render("auth/password-reset-initiation.html", username=username)
152
53a15fe0 153 @base.ratelimit(minutes=15, requests=10)
c7594d58
MT
154 def post(self):
155 username = self.get_argument("username")
156
157 # Fetch account and submit password reset
158 account = self.backend.accounts.get_by_uid(username)
159 if account:
160 with self.db.transaction():
391ede9e 161 account.request_password_reset()
c7594d58
MT
162
163 self.render("auth/password-reset-successful.html")
164
165
391ede9e
MT
166class PasswordResetHandler(AuthenticationMixin, base.BaseHandler):
167 def get(self, uid, reset_code):
168 account = self.backend.accounts.get_by_uid(uid)
169 if not account:
170 raise tornado.web.HTTPError(404, "Could not find account: %s" % uid)
171
172 self.render("auth/password-reset.html", account=account)
173
174 def post(self, uid, reset_code):
175 account = self.backend.accounts.get_by_uid(uid)
176 if not account:
177 raise tornado.web.HTTPError(404, "Could not find account: %s" % uid)
178
179 password1 = self.get_argument("password1")
180 password2 = self.get_argument("password2")
181
182 if not password1 == password2:
183 raise tornado.web.HTTPError(400, "Passwords do not match")
184
185 # Try to perform password reset
186 with self.db.transaction():
187 account.reset_password(reset_code, password1)
188
189 # Login the user straight away after reset was successful
190 self.login(account)
191
192 # Redirect back to /
193 self.redirect("/")
194
195
689effd0 196class APICheckUID(base.APIHandler):
66181c96 197 @base.ratelimit(minutes=1, requests=100)
689effd0
MT
198 def get(self):
199 uid = self.get_argument("uid")
200 result = None
201
202 if not uid:
203 result = "empty"
204
205 # Check if the username is syntactically valid
206 elif not self.backend.accounts.uid_is_valid(uid):
207 result = "invalid"
208
209 # Check if the username is already taken
210 elif self.backend.accounts.uid_exists(uid):
211 result = "taken"
212
213 # Username seems to be okay
214 self.finish({ "result" : result or "ok" })
66181c96
MT
215
216
217class APICheckEmail(base.APIHandler):
218 @base.ratelimit(minutes=1, requests=100)
219 def get(self):
220 email = self.get_argument("email")
221 result = None
222
223 if not email:
224 result = "empty"
225
3095c017
MT
226 elif not self.backend.accounts.mail_is_valid(email):
227 result = "invalid"
228
66181c96
MT
229 # Check if this email address is blacklisted
230 elif self.backend.accounts.mail_is_blacklisted(email):
3095c017 231 result = "blacklisted"
66181c96
MT
232
233 # Check if this email address is already useed
234 elif self.backend.accounts.get_by_mail(email):
235 result = "taken"
236
237 self.finish({ "result" : result or "ok" })