]> git.ipfire.org Git - ipfire.org.git/blame - src/web/auth.py
people: Implement initiation of password reset
[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 41class LoginHandler(AuthenticationMixin, base.BaseHandler):
cfe7d74c 42 @base.blacklisted
08df6527
MT
43 def get(self):
44 next = self.get_argument("next", None)
45
a76ac21e
MT
46 self.render("auth/login.html", next=next,
47 incorrect=False, username=None)
08df6527 48
cfe7d74c 49 @base.blacklisted
372ef119 50 @base.ratelimit(minutes=60, requests=5)
08df6527
MT
51 def post(self):
52 username = self.get_argument("username")
53 password = self.get_argument("password")
a76ac21e 54 next = self.get_argument("next", "/")
08df6527 55
328a7710
MT
56 # Find user
57 account = self.backend.accounts.auth(username, password)
58 if not account:
a76ac21e
MT
59 logging.error("Unknown user or invalid password: %s" % username)
60
61 # Set status to 401
62 self.set_status(401)
63
64 # Render login page again
65 return self.render("auth/login.html",
66 incorrect=True, username=username, next=next,
67 )
328a7710
MT
68
69 # Create session
08df6527 70 with self.db.transaction():
328a7710 71 self.login(account)
08df6527 72
a76ac21e
MT
73 # Redirect the user
74 return self.redirect(next)
08df6527
MT
75
76
77class LogoutHandler(AuthenticationMixin, base.BaseHandler):
78 def get(self):
79 with self.db.transaction():
80 self.logout()
81
82 # Get back to the start page
83 self.redirect("/")
9b8ff27d
MT
84
85
f32dd17f
MT
86class RegisterHandler(base.BaseHandler):
87 @base.blacklisted
88 def get(self):
d521c9df
MT
89 # Redirect logged in users away
90 if self.current_user:
91 self.redirect("/")
92
f32dd17f
MT
93 self.render("auth/register.html")
94
23f84bbc 95 @tornado.gen.coroutine
372ef119 96 @base.ratelimit(minutes=24*60, requests=5)
f32dd17f
MT
97 def post(self):
98 uid = self.get_argument("uid")
99 email = self.get_argument("email")
100
101 first_name = self.get_argument("first_name")
102 last_name = self.get_argument("last_name")
103
23f84bbc
MT
104 # Check if this is a spam account
105 is_spam = yield self.backend.accounts.check_spam(uid, email,
106 address=self.get_remote_ip())
107
108 if is_spam:
109 self.render("auth/register-spam.html")
110 return
111
f32dd17f 112 # Register account
f2ba8a1f
MT
113 try:
114 with self.db.transaction():
115 self.backend.accounts.register(uid, email,
757372cd
MT
116 first_name=first_name, last_name=last_name,
117 country_code=self.current_country_code)
f2ba8a1f 118 except ValueError as e:
09c67399 119 raise tornado.web.HTTPError(400, "%s" % e) from e
f32dd17f
MT
120
121 self.render("auth/register-success.html")
122
123
d8a15b2e
MT
124class ActivateHandler(AuthenticationMixin, base.BaseHandler):
125 def get(self, uid, activation_code):
b4d72c76 126 self.render("auth/activate.html")
d8a15b2e
MT
127
128 def post(self, uid, activation_code):
b4d72c76
MT
129 password1 = self.get_argument("password1")
130 password2 = self.get_argument("password2")
d8a15b2e 131
b4d72c76
MT
132 if not password1 == password2:
133 raise tornado.web.HTTPError(400, "Passwords do not match")
d8a15b2e 134
b4d72c76
MT
135 with self.db.transaction():
136 account = self.backend.accounts.activate(uid, activation_code)
137 if not account:
138 raise tornado.web.HTTPError(400, "Account not found: %s" % uid)
d8a15b2e 139
b4d72c76
MT
140 # Set the new password
141 account.passwd(password1)
d8a15b2e 142
b4d72c76
MT
143 # Create session
144 self.login(account)
d8a15b2e 145
b00cc400
MT
146 # Redirect to success page
147 self.render("auth/activated.html", account=account)
689effd0
MT
148
149
c7594d58
MT
150class PasswordResetInitiationHandler(AuthenticationMixin, base.BaseHandler):
151 def get(self):
152 username = self.get_argument("username", None)
153
154 self.render("auth/password-reset-initiation.html", username=username)
155
156 @base.ratelimit(minutes=60, requests=5)
157 def post(self):
158 username = self.get_argument("username")
159
160 # Fetch account and submit password reset
161 account = self.backend.accounts.get_by_uid(username)
162 if account:
163 with self.db.transaction():
164 account.reset_password()
165
166 self.render("auth/password-reset-successful.html")
167
168
689effd0
MT
169class APICheckUID(base.APIHandler):
170 @base.ratelimit(minutes=10, requests=100)
171 def get(self):
172 uid = self.get_argument("uid")
173 result = None
174
175 if not uid:
176 result = "empty"
177
178 # Check if the username is syntactically valid
179 elif not self.backend.accounts.uid_is_valid(uid):
180 result = "invalid"
181
182 # Check if the username is already taken
183 elif self.backend.accounts.uid_exists(uid):
184 result = "taken"
185
186 # Username seems to be okay
187 self.finish({ "result" : result or "ok" })