Make password reset emails store information in hardcache

Password reset links now stored in the hardcache (they will exist
until the expiration, and never get evicted). Additionally,
if the password reset link has expired, the user will be
redirected to reddit.com/password?expired=true, with an error
message prompting them to try again.
This commit is contained in:
Keith Mitchell
2011-06-17 15:18:50 -07:00
parent 7a0d21e2e0
commit e68b4456a4
5 changed files with 20 additions and 9 deletions

View File

@@ -1665,12 +1665,12 @@ class ApiController(RedditController):
form.set_html(".status", _("try again tomorrow"))
@validatedForm(cache_evt = VCacheKey('reset', ('key',)),
@validatedForm(cache_evt = VHardCacheKey('email-reset', ('key',)),
password = VPassword(['passwd', 'passwd2']))
def POST_resetpassword(self, form, jquery, cache_evt, password):
if form.has_errors('name', errors.EXPIRED):
cache_evt.clear()
form.redirect('/password')
form.redirect('/password?expired=true')
elif form.has_errors('passwd', errors.BAD_PASSWORD):
pass
elif form.has_errors('passwd2', errors.BAD_PASSWORD_MATCH):

View File

@@ -815,7 +815,7 @@ class FormsController(RedditController):
Award.give_if_needed("verified_email", c.user)
return self.redirect(dest)
@validate(cache_evt = VCacheKey('reset', ('key',)),
@validate(cache_evt = VHardCacheKey('email-reset', ('key',)),
key = nop('key'))
def GET_resetpassword(self, cache_evt, key):
"""page hit once a user has been sent a password reset email
@@ -829,10 +829,10 @@ class FormsController(RedditController):
done = False
if not key and request.referer:
referer_path = request.referer.split(g.domain)[-1]
referer_path = request.referer.split(g.domain)[-1]
done = referer_path.startswith(request.fullpath)
elif not getattr(cache_evt, "user", None):
return self.abort404()
return self.redirect("/password?expired=true")
return BoringPage(_("reset password"),
content=ResetPassword(key=key, done=done)).render()

View File

@@ -1154,13 +1154,14 @@ class CachedUser(object):
class VCacheKey(Validator):
def __init__(self, cache_prefix, param, *a, **kw):
self.cache = g.cache
self.cache_prefix = cache_prefix
Validator.__init__(self, param, *a, **kw)
def run(self, key):
c_user = CachedUser(self.cache_prefix, None, key)
if key:
uid = g.cache.get(str(self.cache_prefix + "_" + key))
uid = self.cache.get(str(self.cache_prefix + "_" + key))
if uid:
try:
c_user.user = Account._byID(uid, data = True)
@@ -1169,6 +1170,11 @@ class VCacheKey(Validator):
return c_user
self.set_error(errors.EXPIRED)
class VHardCacheKey(VCacheKey):
def __init__(self, cache_prefix, param, *a, **kw):
VCacheKey.__init__(self, cache_prefix, param, *a, **kw)
self.cache = g.hardcache
class VOneOf(Validator):
def __init__(self, param, options = (), *a, **kw):
Validator.__init__(self, param, *a, **kw)

View File

@@ -83,12 +83,12 @@ def password_email(user):
"""
from r2.lib.pages import PasswordReset
reset_count_key = "reset_count_%s" % user._id
reset_count_key = "email-reset_count_%s" % user._id
g.cache.add(reset_count_key, 0, time=3600 * 12)
if g.cache.incr(reset_count_key) > 3:
return False
reset_count_global = "reset_count_global"
reset_count_global = "email-reset_count_global"
g.cache.add(reset_count_global, 0, time=3600)
if g.cache.incr(reset_count_global) > 1000:
raise ValueError("Somebody's beating the hell out of the password reset box")
@@ -96,7 +96,7 @@ def password_email(user):
key = passhash(randstr(64, reallyrandom = True), user.email)
passlink = 'http://' + g.domain + '/resetpassword/' + key
g.log.info("Generated password reset link: " + passlink)
g.cache.set("reset_%s" % key, user._id, time=1800)
g.hardcache.set("email-reset_%s" % key, user._id, time=3600 * 12)
_system_email(user.email,
PasswordReset(user=user,
passlink=passlink).render(style='email'),

View File

@@ -28,6 +28,11 @@
onsubmit="return post_form(this, 'password');">
<h1>${_("what's my password?")}</h1>
<p>${_("enter your user name below to receive your login information")}</p>
%if request.params.get('expired'):
<span class="error">
${_("password reset link expired. please try again")}
</span>
%endif
<div class="spacer">
<%utils:round_field title="${_('username')}">