Fixing up the email verified stuff to reflect the email_verified stuff!
[mediagoblin.git] / mediagoblin / plugins / basic_auth / views.py
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/>.
16 from itsdangerous import BadSignature
17
18 from mediagoblin import messages
19 from mediagoblin.db.models import User
20 from mediagoblin.decorators import require_active_login
21 from mediagoblin.plugins.basic_auth import forms, tools
22 from mediagoblin.tools.crypto import get_timed_signer_url
23 from mediagoblin.tools.mail import email_debug_message
24 from mediagoblin.tools.response import redirect, render_to_response, render_404
25 from mediagoblin.tools.translate import pass_to_ugettext as _
26
27
28 def 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 user.has_privilege(u'active') is False:
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
90 def 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.has_privilege(u'active'):
129 cp_form = forms.ChangeForgotPassForm(formdata_vars)
130
131 if request.method == 'POST' and cp_form.validate():
132 user.pw_hash = tools.bcrypt_gen_password_hash(
133 cp_form.password.data)
134 user.save()
135
136 messages.add_message(
137 request,
138 messages.INFO,
139 _("You can now log in using your new password."))
140 return redirect(request, 'mediagoblin.auth.login')
141 else:
142 return render_to_response(
143 request,
144 'mediagoblin/plugins/basic_auth/change_fp.html',
145 {'cp_form': cp_form})
146
147 ## Commenting this out temporarily because I'm checking into
148 ## what's going on with user.email_verified.
149 ##
150 ## ... if this commit lasts long enough for anyone but me (cwebber) to
151 ## notice it, they should pester me to remove this or remove it
152 ## themselves ;)
153 #
154 # if not user.email_verified:
155 # messages.add_message(
156 # request, messages.ERROR,
157 # _('You need to verify your email before you can reset your'
158 # ' password.'))
159
160 if not user.status == 'active':
161 messages.add_message(
162 request, messages.ERROR,
163 _('You are no longer an active user. Please contact the system'
164 ' admin to reactivate your accoutn.'))
165
166 return redirect(
167 request, 'index')
168
169
170 def _process_for_token(request):
171 """
172 Checks for tokens in formdata without prior knowledge of request method
173
174 For now, returns whether the userid and token formdata variables exist, and
175 the formdata variables in a hash. Perhaps an object is warranted?
176 """
177 # retrieve the formdata variables
178 if request.method == 'GET':
179 formdata_vars = request.GET
180 else:
181 formdata_vars = request.form
182
183 formdata = {
184 'vars': formdata_vars,
185 'has_token': 'token' in formdata_vars}
186
187 return formdata
188
189
190 @require_active_login
191 def change_pass(request):
192 form = forms.ChangePassForm(request.form)
193 user = request.user
194
195 if request.method == 'POST' and form.validate():
196
197 if not tools.bcrypt_check_password(
198 form.old_password.data, user.pw_hash):
199 form.old_password.errors.append(
200 _('Wrong password'))
201
202 return render_to_response(
203 request,
204 'mediagoblin/plugins/basic_auth/change_pass.html',
205 {'form': form,
206 'user': user})
207
208 # Password matches
209 user.pw_hash = tools.bcrypt_gen_password_hash(
210 form.new_password.data)
211 user.save()
212
213 messages.add_message(
214 request, messages.SUCCESS,
215 _('Your password was changed successfully'))
216
217 return redirect(request, 'mediagoblin.edit.account')
218
219 return render_to_response(
220 request,
221 'mediagoblin/plugins/basic_auth/change_pass.html',
222 {'form': form,
223 'user': user})