Fixing issues in the tests caused by the OPW-Moderation-Update merge
[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
19from mediagoblin.db.models import User
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:
51 user = User.query.filter_by(
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
59 user = User.query.filter_by(
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."))
66 return redirect(request, 'mediagoblin.auth.forgot_password')
67
68 success_message = _("An email has been sent with instructions "
69 "on how to change your password.")
70
71 if user and not(user.email_verified and user.status == 'active'):
72 # Don't send reminder because user is inactive or has no verified email
73 messages.add_message(request,
74 messages.WARNING,
75 _("Could not send password recovery email as your username is in"
76 "active or your account's email address has not been verified."))
77
78 return redirect(request, 'mediagoblin.user_pages.user_home',
79 user=user.username)
80
81 # SUCCESS. Send reminder and return to login page
82 if user:
83 email_debug_message(request)
84 tools.send_fp_verification_email(user, request)
85
86 messages.add_message(request, messages.INFO, success_message)
87 return redirect(request, 'mediagoblin.auth.login')
88
89
90def verify_forgot_password(request):
91 """
92 Check the forgot-password verification and possibly let the user
93 change their password because of it.
94 """
95 # get form data variables, and specifically check for presence of token
96 formdata = _process_for_token(request)
97 if not formdata['has_token']:
98 return render_404(request)
99
100 formdata_vars = formdata['vars']
101
102 # Catch error if token is faked or expired
103 try:
104 token = get_timed_signer_url("mail_verification_token") \
105 .loads(formdata_vars['token'], max_age=10*24*3600)
106 except BadSignature:
107 messages.add_message(
108 request,
109 messages.ERROR,
110 _('The verification key or user id is incorrect.'))
111
112 return redirect(
113 request,
114 'index')
115
116 # check if it's a valid user id
117 user = User.query.filter_by(id=int(token)).first()
118
119 # no user in db
120 if not user:
121 messages.add_message(
122 request, messages.ERROR,
123 _('The user id is incorrect.'))
124 return redirect(
125 request, 'index')
126
127 # check if user active and has email verified
128 if user.email_verified and user.status == 'active':
129
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
148 if not user.email_verified:
149 messages.add_message(
150 request, messages.ERROR,
151 _('You need to verify your email before you can reset your'
152 ' password.'))
153
154 if not user.status == 'active':
155 messages.add_message(
156 request, messages.ERROR,
157 _('You are no longer an active user. Please contact the system'
158 ' admin to reactivate your accoutn.'))
159
160 return redirect(
161 request, 'index')
162
163
164def _process_for_token(request):
165 """
166 Checks for tokens in formdata without prior knowledge of request method
167
168 For now, returns whether the userid and token formdata variables exist, and
169 the formdata variables in a hash. Perhaps an object is warranted?
170 """
171 # retrieve the formdata variables
172 if request.method == 'GET':
173 formdata_vars = request.GET
174 else:
175 formdata_vars = request.form
176
177 formdata = {
178 'vars': formdata_vars,
179 'has_token': 'token' in formdata_vars}
180
181 return formdata
af665c4e
RE
182
183
184@require_active_login
185def change_pass(request):
186 form = forms.ChangePassForm(request.form)
187 user = request.user
188
189 if request.method == 'POST' and form.validate():
190
191 if not tools.bcrypt_check_password(
192 form.old_password.data, user.pw_hash):
193 form.old_password.errors.append(
194 _('Wrong password'))
195
196 return render_to_response(
197 request,
198 'mediagoblin/plugins/basic_auth/change_pass.html',
199 {'form': form,
200 'user': user})
201
202 # Password matches
203 user.pw_hash = tools.bcrypt_gen_password_hash(
204 form.new_password.data)
205 user.save()
206
207 messages.add_message(
208 request, messages.SUCCESS,
209 _('Your password was changed successfully'))
210
211 return redirect(request, 'mediagoblin.edit.account')
212
213 return render_to_response(
214 request,
215 'mediagoblin/plugins/basic_auth/change_pass.html',
216 {'form': form,
217 'user': user})