]> git.ipfire.org Git - ipfire.org.git/blob - src/web/auth.py
accounts: Drop StopForumSpam
[ipfire.org.git] / src / web / auth.py
1 #!/usr/bin/python
2
3 import logging
4 import tornado.web
5
6 from . import base
7
8 class 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("Cache-Control", "no-store")
15
16
17 class AuthenticationMixin(CacheMixin):
18 def login(self, account):
19 # User has logged in, create a session
20 session_id, session_expires = self.backend.accounts.create_session(
21 account, self.request.host)
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, secure=True)
30
31 def logout(self):
32 session_id = self.get_cookie("session_id")
33 if not session_id:
34 return
35
36 success = self.backend.accounts.destroy_session(session_id, self.request.host)
37 if success:
38 self.clear_cookie("session_id")
39
40
41 class LoginHandler(AuthenticationMixin, base.BaseHandler):
42 def get(self):
43 next = self.get_argument("next", None)
44
45 self.render("auth/login.html", next=next,
46 incorrect=False, username=None)
47
48 @base.ratelimit(minutes=15, requests=10)
49 def post(self):
50 username = self.get_argument("username")
51 password = self.get_argument("password")
52 next = self.get_argument("next", "/")
53
54 # Find user
55 account = self.backend.accounts.auth(username, password)
56 if not account:
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 )
66
67 # Create session
68 with self.db.transaction():
69 self.login(account)
70
71 # Redirect the user
72 return self.redirect(next)
73
74
75 class 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("/")
82
83
84 class RegisterHandler(CacheMixin, base.BaseHandler):
85 def get(self):
86 # Redirect logged in users away
87 if self.current_user:
88 self.redirect("/")
89 return
90
91 self.render("auth/register.html")
92
93 @base.ratelimit(minutes=15, requests=5)
94 async def post(self):
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
101 # Register account
102 try:
103 with self.db.transaction():
104 self.backend.accounts.register(uid, email,
105 first_name=first_name, last_name=last_name,
106 country_code=self.current_country_code)
107 except ValueError as e:
108 raise tornado.web.HTTPError(400, "%s" % e) from e
109
110 self.render("auth/register-success.html")
111
112
113 class ActivateHandler(AuthenticationMixin, base.BaseHandler):
114 def get(self, uid, activation_code):
115 self.render("auth/activate.html")
116
117 def post(self, uid, activation_code):
118 password1 = self.get_argument("password1")
119 password2 = self.get_argument("password2")
120
121 if not password1 == password2:
122 raise tornado.web.HTTPError(400, "Passwords do not match")
123
124 with self.db.transaction():
125 account = self.backend.accounts.activate(uid, activation_code)
126 if not account:
127 raise tornado.web.HTTPError(400, "Account not found: %s" % uid)
128
129 # Set the new password
130 account.passwd(password1)
131
132 # Create session
133 self.login(account)
134
135 # Redirect to success page
136 self.render("auth/activated.html", account=account)
137
138
139 class PasswordResetInitiationHandler(CacheMixin, base.BaseHandler):
140 def get(self):
141 username = self.get_argument("username", None)
142
143 self.render("auth/password-reset-initiation.html", username=username)
144
145 @base.ratelimit(minutes=15, requests=10)
146 def post(self):
147 username = self.get_argument("username")
148
149 # Fetch account and submit password reset
150 account = self.backend.accounts.get_by_uid(username)
151 if account:
152 with self.db.transaction():
153 account.request_password_reset()
154
155 self.render("auth/password-reset-successful.html")
156
157
158 class PasswordResetHandler(AuthenticationMixin, base.BaseHandler):
159 def get(self, uid, reset_code):
160 account = self.backend.accounts.get_by_uid(uid)
161 if not account:
162 raise tornado.web.HTTPError(404, "Could not find account: %s" % uid)
163
164 self.render("auth/password-reset.html", account=account)
165
166 def post(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 password1 = self.get_argument("password1")
172 password2 = self.get_argument("password2")
173
174 if not password1 == password2:
175 raise tornado.web.HTTPError(400, "Passwords do not match")
176
177 # Try to perform password reset
178 with self.db.transaction():
179 account.reset_password(reset_code, password1)
180
181 # Login the user straight away after reset was successful
182 self.login(account)
183
184 # Redirect back to /
185 self.redirect("/")
186
187
188 class APICheckUID(base.APIHandler):
189 @base.ratelimit(minutes=1, requests=100)
190 def get(self):
191 uid = self.get_argument("uid")
192 result = None
193
194 if not uid:
195 result = "empty"
196
197 # Check if the username is syntactically valid
198 elif not self.backend.accounts.uid_is_valid(uid):
199 result = "invalid"
200
201 # Check if the username is already taken
202 elif self.backend.accounts.uid_exists(uid):
203 result = "taken"
204
205 # Username seems to be okay
206 self.finish({ "result" : result or "ok" })
207
208
209 class APICheckEmail(base.APIHandler):
210 @base.ratelimit(minutes=1, requests=100)
211 def get(self):
212 email = self.get_argument("email")
213 result = None
214
215 if not email:
216 result = "empty"
217
218 elif not self.backend.accounts.mail_is_valid(email):
219 result = "invalid"
220
221 # Check if this email address is blacklisted
222 elif self.backend.accounts.mail_is_blacklisted(email):
223 result = "blacklisted"
224
225 # Check if this email address is already useed
226 elif self.backend.accounts.get_by_mail(email):
227 result = "taken"
228
229 self.finish({ "result" : result or "ok" })