86d7eaef88104351ce6a01fdbd2d564dcba71254
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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
18 from mediagoblin
import messages
19 from mediagoblin
.db
.models
import LocalUser
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 _
28 def forgot_password(request
):
32 Sends an email with an url to renew forgotten password.
33 Use GET querystring parameter 'username' to pre-populate the input field
35 fp_form
= forms
.ForgotPassForm(request
.form
,
36 username
=request
.args
.get('username'))
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',
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
51 user
= LocalUser
.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.")
58 else: # found by username
59 user
= LocalUser
.query
.filter_by(
60 username
=fp_form
.username
.data
).first()
63 messages
.add_message(request
,
65 _("Couldn't find someone with that username."))
66 return redirect(request
,
67 'mediagoblin.plugins.basic_auth.forgot_password')
69 success_message
= _("An email has been sent with instructions "
70 "on how to change your password.")
72 if user
and user
.has_privilege(u
'active') is False:
73 # Don't send reminder because user is inactive or has no verified email
74 messages
.add_message(request
,
76 _("Could not send password recovery email as your username is in"
77 "active or your account's email address has not been verified."))
79 return redirect(request
, 'mediagoblin.user_pages.user_home',
82 # SUCCESS. Send reminder and return to login page
84 email_debug_message(request
)
85 tools
.send_fp_verification_email(user
, request
)
87 messages
.add_message(request
, messages
.INFO
, success_message
)
88 return redirect(request
, 'mediagoblin.auth.login')
91 def verify_forgot_password(request
):
93 Check the forgot-password verification and possibly let the user
94 change their password because of it.
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
)
101 formdata_vars
= formdata
['vars']
103 # Catch error if token is faked or expired
105 token
= get_timed_signer_url("mail_verification_token") \
106 .loads(formdata_vars
['token'], max_age
=10*24*3600)
108 messages
.add_message(
111 _('The verification key or user id is incorrect.'))
117 # check if it's a valid user id
118 user
= LocalUser
.query
.filter_by(id=int(token
)).first()
122 messages
.add_message(
123 request
, messages
.ERROR
,
124 _('The user id is incorrect.'))
128 # check if user active and has email verified
129 if user
.has_privilege(u
'active'):
130 cp_form
= forms
.ChangeForgotPassForm(formdata_vars
)
132 if request
.method
== 'POST' and cp_form
.validate():
133 user
.pw_hash
= tools
.bcrypt_gen_password_hash(
134 cp_form
.password
.data
)
137 messages
.add_message(
140 _("You can now log in using your new password."))
141 return redirect(request
, 'mediagoblin.auth.login')
143 return render_to_response(
145 'mediagoblin/plugins/basic_auth/change_fp.html',
146 {'cp_form': cp_form
})
148 ## Commenting this out temporarily because I'm checking into
149 ## what's going on with user.email_verified.
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
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'
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'
165 ' admin to reactivate your account.'))
171 def _process_for_token(request
):
173 Checks for tokens in formdata without prior knowledge of request method
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?
178 # retrieve the formdata variables
179 if request
.method
== 'GET':
180 formdata_vars
= request
.GET
182 formdata_vars
= request
.form
185 'vars': formdata_vars
,
186 'has_token': 'token' in formdata_vars
}
191 @require_active_login
192 def change_pass(request
):
193 form
= forms
.ChangePassForm(request
.form
)
196 if request
.method
== 'POST' and form
.validate():
198 if not tools
.bcrypt_check_password(
199 form
.old_password
.data
, user
.pw_hash
):
200 form
.old_password
.errors
.append(
203 return render_to_response(
205 'mediagoblin/plugins/basic_auth/change_pass.html',
210 user
.pw_hash
= tools
.bcrypt_gen_password_hash(
211 form
.new_password
.data
)
214 messages
.add_message(
215 request
, messages
.SUCCESS
,
216 _('Your password was changed successfully'))
218 return redirect(request
, 'mediagoblin.edit.account')
220 return render_to_response(
222 'mediagoblin/plugins/basic_auth/change_pass.html',