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 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 _
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
= 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.")
58 else: # found by username
59 user
= User
.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
, 'mediagoblin.auth.forgot_password')
68 success_message
= _("An email has been sent with instructions "
69 "on how to change your password.")
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
,
75 _("Could not send password recovery email as your username is in"
76 "active or your account's email address has not been verified."))
78 return redirect(request
, 'mediagoblin.user_pages.user_home',
81 # SUCCESS. Send reminder and return to login page
83 email_debug_message(request
)
84 tools
.send_fp_verification_email(user
, request
)
86 messages
.add_message(request
, messages
.INFO
, success_message
)
87 return redirect(request
, 'mediagoblin.auth.login')
90 def verify_forgot_password(request
):
92 Check the forgot-password verification and possibly let the user
93 change their password because of it.
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
)
100 formdata_vars
= formdata
['vars']
102 # Catch error if token is faked or expired
104 token
= get_timed_signer_url("mail_verification_token") \
105 .loads(formdata_vars
['token'], max_age
=10*24*3600)
107 messages
.add_message(
110 _('The verification key or user id is incorrect.'))
116 # check if it's a valid user id
117 user
= User
.query
.filter_by(id=int(token
)).first()
121 messages
.add_message(
122 request
, messages
.ERROR
,
123 _('The user id is incorrect.'))
127 # check if user active and has email verified
128 if user
.has_privilege(u
'active'):
129 cp_form
= forms
.ChangeForgotPassForm(formdata_vars
)
131 if request
.method
== 'POST' and cp_form
.validate():
132 user
.pw_hash
= tools
.bcrypt_gen_password_hash(
133 cp_form
.password
.data
)
136 messages
.add_message(
139 _("You can now log in using your new password."))
140 return redirect(request
, 'mediagoblin.auth.login')
142 return render_to_response(
144 'mediagoblin/plugins/basic_auth/change_fp.html',
145 {'cp_form': cp_form
})
147 ## Commenting this out temporarily because I'm checking into
148 ## what's going on with user.email_verified.
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
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'
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.'))
170 def _process_for_token(request
):
172 Checks for tokens in formdata without prior knowledge of request method
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?
177 # retrieve the formdata variables
178 if request
.method
== 'GET':
179 formdata_vars
= request
.GET
181 formdata_vars
= request
.form
184 'vars': formdata_vars
,
185 'has_token': 'token' in formdata_vars
}
190 @require_active_login
191 def change_pass(request
):
192 form
= forms
.ChangePassForm(request
.form
)
195 if request
.method
== 'POST' and form
.validate():
197 if not tools
.bcrypt_check_password(
198 form
.old_password
.data
, user
.pw_hash
):
199 form
.old_password
.errors
.append(
202 return render_to_response(
204 'mediagoblin/plugins/basic_auth/change_pass.html',
209 user
.pw_hash
= tools
.bcrypt_gen_password_hash(
210 form
.new_password
.data
)
213 messages
.add_message(
214 request
, messages
.SUCCESS
,
215 _('Your password was changed successfully'))
217 return redirect(request
, 'mediagoblin.edit.account')
219 return render_to_response(
221 'mediagoblin/plugins/basic_auth/change_pass.html',