Issue #5394: Wrong url for forgot_password in basic_auth plugin
[mediagoblin.git] / mediagoblin / plugins / basic_auth / views.py
CommitLineData
aeae6cc2
RE
1# GNU MediaGoblin -- federated, autonomous media hosting
2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16from itsdangerous import BadSignature
17
18from mediagoblin import messages
d88fcb03 19from mediagoblin.db.models import LocalUser
af665c4e 20from mediagoblin.decorators import require_active_login
aeae6cc2
RE
21from mediagoblin.plugins.basic_auth import forms, tools
22from mediagoblin.tools.crypto import get_timed_signer_url
23from mediagoblin.tools.mail import email_debug_message
24from mediagoblin.tools.response import redirect, render_to_response, render_404
25from mediagoblin.tools.translate import pass_to_ugettext as _
26
27
28def forgot_password(request):
29 """
30 Forgot password view
31
32 Sends an email with an url to renew forgotten password.
33 Use GET querystring parameter 'username' to pre-populate the input field
34 """
35 fp_form = forms.ForgotPassForm(request.form,
36 username=request.args.get('username'))
37
38 if not (request.method == 'POST' and fp_form.validate()):
39 # Either GET request, or invalid form submitted. Display the template
40 return render_to_response(request,
41 'mediagoblin/plugins/basic_auth/forgot_password.html',
42 {'fp_form': fp_form})
43
44 # If we are here: method == POST and form is valid. username casing
45 # has been sanitized. Store if a user was found by email. We should
46 # not reveal if the operation was successful then as we don't want to
47 # leak if an email address exists in the system.
48 found_by_email = '@' in fp_form.username.data
49
50 if found_by_email:
d88fcb03 51 user = LocalUser.query.filter_by(
aeae6cc2
RE
52 email=fp_form.username.data).first()
53 # Don't reveal success in case the lookup happened by email address.
54 success_message = _("If that email address (case sensitive!) is "
55 "registered an email has been sent with "
56 "instructions on how to change your password.")
57
58 else: # found by username
d88fcb03 59 user = LocalUser.query.filter_by(
aeae6cc2
RE
60 username=fp_form.username.data).first()
61
62 if user is None:
63 messages.add_message(request,
64 messages.WARNING,
65 _("Couldn't find someone with that username."))
64c035b3
CAW
66 return redirect(request,
67 'mediagoblin.plugins.basic_auth.forgot_password')
aeae6cc2
RE
68
69 success_message = _("An email has been sent with instructions "
70 "on how to change your password.")
71
6180e3a9 72 if user and user.has_privilege(u'active') is False:
aeae6cc2
RE
73 # Don't send reminder because user is inactive or has no verified email
74 messages.add_message(request,
75 messages.WARNING,
76 _("Could not send password recovery email as your username is in"
77 "active or your account's email address has not been verified."))
78
79 return redirect(request, 'mediagoblin.user_pages.user_home',
80 user=user.username)
81
82 # SUCCESS. Send reminder and return to login page
83 if user:
84 email_debug_message(request)
85 tools.send_fp_verification_email(user, request)
86
87 messages.add_message(request, messages.INFO, success_message)
88 return redirect(request, 'mediagoblin.auth.login')
89
90
91def verify_forgot_password(request):
92 """
93 Check the forgot-password verification and possibly let the user
94 change their password because of it.
95 """
96 # get form data variables, and specifically check for presence of token
97 formdata = _process_for_token(request)
98 if not formdata['has_token']:
99 return render_404(request)
100
101 formdata_vars = formdata['vars']
102
103 # Catch error if token is faked or expired
104 try:
105 token = get_timed_signer_url("mail_verification_token") \
106 .loads(formdata_vars['token'], max_age=10*24*3600)
107 except BadSignature:
108 messages.add_message(
109 request,
110 messages.ERROR,
111 _('The verification key or user id is incorrect.'))
112
113 return redirect(
114 request,
115 'index')
116
117 # check if it's a valid user id
d88fcb03 118 user = LocalUser.query.filter_by(id=int(token)).first()
aeae6cc2
RE
119
120 # no user in db
121 if not user:
122 messages.add_message(
123 request, messages.ERROR,
124 _('The user id is incorrect.'))
125 return redirect(
126 request, 'index')
127
128 # check if user active and has email verified
6180e3a9 129 if user.has_privilege(u'active'):
33b5cebe 130 cp_form = forms.ChangeForgotPassForm(formdata_vars)
aeae6cc2
RE
131
132 if request.method == 'POST' and cp_form.validate():
133 user.pw_hash = tools.bcrypt_gen_password_hash(
134 cp_form.password.data)
135 user.save()
136
137 messages.add_message(
138 request,
139 messages.INFO,
140 _("You can now log in using your new password."))
141 return redirect(request, 'mediagoblin.auth.login')
142 else:
143 return render_to_response(
144 request,
145 'mediagoblin/plugins/basic_auth/change_fp.html',
146 {'cp_form': cp_form})
147
6180e3a9
CAW
148 ## Commenting this out temporarily because I'm checking into
149 ## what's going on with user.email_verified.
150 ##
151 ## ... if this commit lasts long enough for anyone but me (cwebber) to
152 ## notice it, they should pester me to remove this or remove it
153 ## themselves ;)
154 #
155 # if not user.email_verified:
156 # messages.add_message(
157 # request, messages.ERROR,
158 # _('You need to verify your email before you can reset your'
159 # ' password.'))
aeae6cc2
RE
160
161 if not user.status == 'active':
162 messages.add_message(
163 request, messages.ERROR,
164 _('You are no longer an active user. Please contact the system'
4491174d 165 ' admin to reactivate your account.'))
aeae6cc2
RE
166
167 return redirect(
168 request, 'index')
169
170
171def _process_for_token(request):
172 """
173 Checks for tokens in formdata without prior knowledge of request method
174
175 For now, returns whether the userid and token formdata variables exist, and
176 the formdata variables in a hash. Perhaps an object is warranted?
177 """
178 # retrieve the formdata variables
179 if request.method == 'GET':
180 formdata_vars = request.GET
181 else:
182 formdata_vars = request.form
183
184 formdata = {
185 'vars': formdata_vars,
186 'has_token': 'token' in formdata_vars}
187
188 return formdata
af665c4e
RE
189
190
191@require_active_login
192def change_pass(request):
193 form = forms.ChangePassForm(request.form)
194 user = request.user
195
196 if request.method == 'POST' and form.validate():
197
198 if not tools.bcrypt_check_password(
199 form.old_password.data, user.pw_hash):
200 form.old_password.errors.append(
201 _('Wrong password'))
202
203 return render_to_response(
204 request,
205 'mediagoblin/plugins/basic_auth/change_pass.html',
206 {'form': form,
207 'user': user})
208
209 # Password matches
210 user.pw_hash = tools.bcrypt_gen_password_hash(
211 form.new_password.data)
212 user.save()
213
214 messages.add_message(
215 request, messages.SUCCESS,
216 _('Your password was changed successfully'))
217
218 return redirect(request, 'mediagoblin.edit.account')
219
220 return render_to_response(
221 request,
222 'mediagoblin/plugins/basic_auth/change_pass.html',
223 {'form': form,
224 'user': user})