From e26f3aa3c4e6e034d0d0cb7e945d55e832d5951b Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Thu, 11 Feb 2010 22:09:31 +0200 Subject: [PATCH 1/3] Added "reset password" e-mail template. --- scribeengine/controllers/account.py | 53 ++++++++++++++++++++++ scribeengine/templates/email/activate.mako | 6 +-- scribeengine/templates/email/reset.mako | 11 +++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 scribeengine/templates/email/reset.mako diff --git a/scribeengine/controllers/account.py b/scribeengine/controllers/account.py index 97a14e2..bfd47df 100644 --- a/scribeengine/controllers/account.py +++ b/scribeengine/controllers/account.py @@ -117,6 +117,59 @@ class AccountController(BaseController): u'registration.', u'success') h.redirect_to(h.url_for(controller=u'account', action=u'login')) + def reset(self): + c.page_title = u'Reset Password' + return render(u'/account/reset.mako') + + @jsvalidate(u'reset-form') + def reset_jsschema(self): + return { + u'email': JSEmail(required=True, message=u'You haven\'t typed in an e-mail address.'), + u'password': JSString(required=True, message=u'You haven\'t typed in a password.'), + u'confirm-password': JSString(required=True, equalTo=u'#password', message=u'Your passwords don\'t match.') + } + + def reset_schema(self): + return { + 'email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in an e-mail address.'}), + 'password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}), + 'confirm': [FieldsMatch('password', 'confirm-passsword', messages={'invalid': u'Your passwords don\'t match.'})] + } + + def reset_POST(self): + email = c.form_values[u'email'] + user = Session.query(User).filter_by(email=email).first() + if not user: + h.flash.set_message(u'Your e-mail address is not in the system.', u'error') + else: + activation_code = u''.join(random.sample(string.letters + string.digits, 40)) + user.password = utils.hash_password(activation_code), + user.activation_key = activation_code + user.modified = datetime.now() + Session.add(user) + Session.commit() + blog_mail = Session.query(Variable).get(u'blog mail') + blog_title = Session.query(Variable).get(u'blog title') + blog_host = Session.query(Variable).get(u'blog host') + if not blog_host: + url = u'%s://%s' % (request.environ[u'wsgi.url_scheme'], + request.environ[u'HTTP_HOST']) + blog_host = Variable(key=u'blog host', value=url) + Session.add(blog_host) + Session.commit() + utils.send_mail(u'/email/activate.mako', u'%s <%s>' % (user.nick, user.email), + u'%s <%s>' % (blog_mail.value, blog_title.value), + u'[%s] Activate your account!' % blog_title.value, + { + 'user': user, + 'blog_title': blog_title.value, + 'blog_host': blog_host.value + }) + h.flash.set_message(u'An e-mail has been sent to your e-mail address. ' + u'Please activate your account by clicking on the link in your ' + u'e-mail.', u'success') + h.redirect_to('/') + def login(self): c.page_title = u'Login' return render(u'/account/login.mako') diff --git a/scribeengine/templates/email/activate.mako b/scribeengine/templates/email/activate.mako index 30e7d57..3b96404 100644 --- a/scribeengine/templates/email/activate.mako +++ b/scribeengine/templates/email/activate.mako @@ -4,14 +4,14 @@ You have just registered on ${c.blog_title}, but before you continue, you will need to activate your account. You can do this by simply clicking on the link below, or copying and pasting it into your browser. -${c.blog_host}${h.url_for(controller=u'admin', action=u'activate', id=c.user.id, code=c.user.activation_key)} +${c.blog_host}${h.url_for(controller=u'account', action=u'activate', id=c.user.id, code=c.user.activation_key)} If this is not you, simply leave this e-mail, and the account will expire after 72 hours. If you left this e-mail for too long and want to continue with the -registration process, click on the link below, type in your e-mail addres, and +registration process, click on the link below, type in your e-mail address, and another e-mail will be sent to you to activate your account. -${c.blog_host}${h.url_for(controller=u'admin', action=u'reset', id=c.user.id)} +${c.blog_host}${h.url_for(controller=u'account', action=u'reset', id=c.user.id)} Once you have completed the registration process you will be able to comment on the posts on the site. diff --git a/scribeengine/templates/email/reset.mako b/scribeengine/templates/email/reset.mako new file mode 100644 index 0000000..b3f1c12 --- /dev/null +++ b/scribeengine/templates/email/reset.mako @@ -0,0 +1,11 @@ +Dear ${c.user.nick}, + +You have just reset your password on ${c.blog_title}, but before you continue, +you will need to activate your account. You can do this by simply clicking on +the link below, or copying and pasting it into your browser. + +${c.blog_host}${h.url_for(controller=u'account', action=u'activate', id=c.user.id, code=c.user.activation_key)} + +Kind regards, + +${c.blog_title} Team From 4cb84692338e11d4255b064d3ed42333c844cf5a Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 14 Feb 2010 23:26:06 +0200 Subject: [PATCH 2/3] Added reset password stuff. --- scribeengine/controllers/account.py | 92 +++++++++++++------- scribeengine/templates/account/login.mako | 14 +-- scribeengine/templates/account/password.mako | 21 +++++ scribeengine/templates/account/register.mako | 14 +-- scribeengine/templates/account/reset.mako | 17 ++++ scribeengine/templates/email/reset.mako | 2 +- 6 files changed, 117 insertions(+), 43 deletions(-) create mode 100644 scribeengine/templates/account/password.mako create mode 100644 scribeengine/templates/account/reset.mako diff --git a/scribeengine/controllers/account.py b/scribeengine/controllers/account.py index bfd47df..9306051 100644 --- a/scribeengine/controllers/account.py +++ b/scribeengine/controllers/account.py @@ -46,24 +46,24 @@ class AccountController(BaseController): @jsvalidate(u'register-form') def register_jsschema(self): return { - u'email': JSEmail(required=True, message=u'You haven\'t typed in an e-mail address.'), - u'password': JSString(required=True, message=u'You haven\'t typed in a password.'), - u'confirm-password': JSString(required=True, equalTo=u'#password', message=u'Your passwords don\'t match.') + u'register-email': JSEmail(required=True, message=u'You haven\'t typed in an e-mail address.'), + u'register-password': JSString(required=True, message=u'You haven\'t typed in a password.'), + u'register-confirm': JSString(required=True, equalTo=u'#password', message=u'Your passwords don\'t match.') } def register_schema(self): return { - 'email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in an e-mail address.'}), - 'password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}), - 'confirm': [FieldsMatch('password', 'confirm-passsword', messages={'invalid': u'Your passwords don\'t match.'})] + 'register-email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in an e-mail address.'}), + 'register-password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}), + 'confirm-password': [FieldsMatch('register-password', 'register-confirm', messages={'invalid': u'Your passwords don\'t match.'})] } def register_POST(self): activation_code = u''.join(random.sample(string.letters + string.digits, 40)) user = User( - nick=c.form_values[u'nick'], - email=c.form_values[u'email'], - password=utils.hash_password(c.form_values[u'password']), + nick=c.form_values[u'register-nick'], + email=c.form_values[u'register-email'], + password=utils.hash_password(c.form_values[u'register-password']), activation_key=activation_code ) Session.add(user) @@ -121,29 +121,24 @@ class AccountController(BaseController): c.page_title = u'Reset Password' return render(u'/account/reset.mako') - @jsvalidate(u'reset-form') + @jsvalidate(u'account-reset') def reset_jsschema(self): return { - u'email': JSEmail(required=True, message=u'You haven\'t typed in an e-mail address.'), - u'password': JSString(required=True, message=u'You haven\'t typed in a password.'), - u'confirm-password': JSString(required=True, equalTo=u'#password', message=u'Your passwords don\'t match.') + u'reset-email': JSEmail(required=True, message=u'You haven\'t typed in a valid e-mail address.') } def reset_schema(self): return { - 'email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in an e-mail address.'}), - 'password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}), - 'confirm': [FieldsMatch('password', 'confirm-passsword', messages={'invalid': u'Your passwords don\'t match.'})] + 'reset-email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in a valid e-mail address.'}) } def reset_POST(self): - email = c.form_values[u'email'] + email = c.form_values[u'reset-email'] user = Session.query(User).filter_by(email=email).first() if not user: h.flash.set_message(u'Your e-mail address is not in the system.', u'error') else: activation_code = u''.join(random.sample(string.letters + string.digits, 40)) - user.password = utils.hash_password(activation_code), user.activation_key = activation_code user.modified = datetime.now() Session.add(user) @@ -157,40 +152,77 @@ class AccountController(BaseController): blog_host = Variable(key=u'blog host', value=url) Session.add(blog_host) Session.commit() - utils.send_mail(u'/email/activate.mako', u'%s <%s>' % (user.nick, user.email), + utils.send_mail(u'/email/reset.mako', u'%s <%s>' % (user.nick, user.email), u'%s <%s>' % (blog_mail.value, blog_title.value), - u'[%s] Activate your account!' % blog_title.value, + u'[%s] Reset your password!' % blog_title.value, { 'user': user, 'blog_title': blog_title.value, 'blog_host': blog_host.value }) h.flash.set_message(u'An e-mail has been sent to your e-mail address. ' - u'Please activate your account by clicking on the link in your ' + u'Please reset your password by clicking on the link in your ' u'e-mail.', u'success') - h.redirect_to('/') + h.redirect_to('/account/reset') + + def password(self, id=None): + if not id or not request.GET.get(u'code'): + h.flash.set_message(u'There was a problem with your activation code, please reset your password again.', u'error') + h.redirect_to(h.url_for(controller=u'account', action=u'login')) + c.user = Session.query(User).get(id) + if not c.user: + h.flash.set_message(u'There was a problem with your account, please reset your password again.', u'error') + h.redirect_to(h.url_for(controller=u'account', action=u'login')) + c.page_title = u'Change Password' + return render(u'/account/password.mako') + + @jsvalidate(u'account-password') + def password_jsschema(self): + return { + u'password-password': JSString(required=True, message=u'You haven\'t typed in a password.'), + u'password-confirm': JSString(required=True, equalTo=u'#password-password', message=u'Your passwords don\'t match.') + } + + def password_schema(self): + return { + 'user_id': Int(), + 'password-password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}), + 'confirm-password': [FieldsMatch('password-password', 'password-confirm', messages={'invalid': u'Your passwords don\'t match.'})] + } + + def password_POST(self): + user = Session.query(User).get(c.form_values[u'user_id']) + if not user: + h.flash.set_message(u'There was a problem with your account, please reset your password again.', u'error') + h.redirect_to(h.url_for(controller=u'account', action=u'login')) + user.password = utils.hash_password(c.form_values[u'password-password']) + user.modified = datetime.now() + Session.add(user) + Session.commit() + h.flash.set_message(u'Successfully updated your password. Please login with your new password.', u'success') + h.redirect_to('/account/login') def login(self): c.page_title = u'Login' return render(u'/account/login.mako') - @jsvalidate(u'login-form') + @jsvalidate(u'account-login') def login_jsschema(self): return { - u'email': JSEmail(required=True, message=u'You haven\'t typed in an e-mail address.'), - u'password': JSString(required=True, message=u'You haven\'t typed in a password.') + u'login-email': JSEmail(required=True, message=u'You haven\'t typed in an e-mail address.'), + u'login-password': JSString(required=True, message=u'You haven\'t typed in a password.') } def login_schema(self): return { - 'email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in an e-mail address.'}), - 'password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}) + 'login-email': Email(not_empty=True, messages={'empty': u'You haven\'t typed in an e-mail address.'}), + 'login-password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}) } def login_POST(self): - log.debug('Logging in as "%s" with password "%s"', c.form_values[u'email'], c.form_values[u'password']) - user = Session.query(User).filter_by(email=c.form_values[u'email']).first() - password = utils.hash_password(c.form_values[u'password']) + log.debug('Logging in as "%s" with password "%s"', c.form_values[u'login-email'], c.form_values[u'login-password']) + user = Session.query(User).filter_by(email=c.form_values[u'login-email']).first() + password = utils.hash_password(c.form_values[u'login-password']) if not user or user.password != password: log.debug('Username or password are incorrect.') h.flash.set_message(u'Your username or password are incorrect.', u'error') diff --git a/scribeengine/templates/account/login.mako b/scribeengine/templates/account/login.mako index ed11b2c..bb94d38 100644 --- a/scribeengine/templates/account/login.mako +++ b/scribeengine/templates/account/login.mako @@ -3,19 +3,23 @@

Log in

<%include file="/errors.mako"/> -
+
- +
- +
- - No account? Register now! + + + Register now + or + reset your password. +
diff --git a/scribeengine/templates/account/password.mako b/scribeengine/templates/account/password.mako new file mode 100644 index 0000000..12ccff5 --- /dev/null +++ b/scribeengine/templates/account/password.mako @@ -0,0 +1,21 @@ +<%inherit file="/base.mako"/> + <%include file="/flash.mako"/> +
+

New password

+ <%include file="/errors.mako"/> +
+
+
+ + +
+
+ + +
+
+ +
+
+
+
diff --git a/scribeengine/templates/account/register.mako b/scribeengine/templates/account/register.mako index 2542354..3c858ee 100644 --- a/scribeengine/templates/account/register.mako +++ b/scribeengine/templates/account/register.mako @@ -3,26 +3,26 @@

Register

<%include file="/errors.mako"/> -
+
- +
- +
- +
- - + +
- +
diff --git a/scribeengine/templates/account/reset.mako b/scribeengine/templates/account/reset.mako new file mode 100644 index 0000000..4087466 --- /dev/null +++ b/scribeengine/templates/account/reset.mako @@ -0,0 +1,17 @@ +<%inherit file="/base.mako"/> + <%include file="/flash.mako"/> +
+

Reset your password

+ <%include file="/errors.mako"/> +
+
+
+ + +
+
+ +
+
+
+
diff --git a/scribeengine/templates/email/reset.mako b/scribeengine/templates/email/reset.mako index b3f1c12..40ab706 100644 --- a/scribeengine/templates/email/reset.mako +++ b/scribeengine/templates/email/reset.mako @@ -4,7 +4,7 @@ You have just reset your password on ${c.blog_title}, but before you continue, you will need to activate your account. You can do this by simply clicking on the link below, or copying and pasting it into your browser. -${c.blog_host}${h.url_for(controller=u'account', action=u'activate', id=c.user.id, code=c.user.activation_key)} +${c.blog_host}${h.url_for(controller=u'account', action=u'password', id=c.user.id, code=c.user.activation_key)} Kind regards, From c6fb5feffb95c394192e91d704b0caae9299b2b8 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Tue, 16 Feb 2010 22:10:09 +0200 Subject: [PATCH 3/3] Last few bug fixes. --- scribeengine/controllers/account.py | 13 +++++++++---- scribeengine/templates/account/password.mako | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/scribeengine/controllers/account.py b/scribeengine/controllers/account.py index 9306051..f8b330e 100644 --- a/scribeengine/controllers/account.py +++ b/scribeengine/controllers/account.py @@ -25,6 +25,8 @@ import string import random from datetime import datetime +from formencode.validators import Int + from scribeengine.lib.base import * from scribeengine.lib.validation.client import JSString, JSEmail from scribeengine.lib.validation.server import UnicodeString, Email, FieldsMatch @@ -163,7 +165,7 @@ class AccountController(BaseController): h.flash.set_message(u'An e-mail has been sent to your e-mail address. ' u'Please reset your password by clicking on the link in your ' u'e-mail.', u'success') - h.redirect_to('/account/reset') + h.redirect_to('/account/login') def password(self, id=None): if not id or not request.GET.get(u'code'): @@ -173,6 +175,9 @@ class AccountController(BaseController): if not c.user: h.flash.set_message(u'There was a problem with your account, please reset your password again.', u'error') h.redirect_to(h.url_for(controller=u'account', action=u'login')) + if c.user.activation_key != request.GET.get(u'code'): + h.flash.set_message(u'There was a problem with your activation code, please reset your password again.', u'error') + h.redirect_to(h.url_for(controller=u'account', action=u'login')) c.page_title = u'Change Password' return render(u'/account/password.mako') @@ -185,17 +190,17 @@ class AccountController(BaseController): def password_schema(self): return { - 'user_id': Int(), 'password-password': UnicodeString(not_empty=True, messages={'empty': u'You haven\'t typed in a password.'}), 'confirm-password': [FieldsMatch('password-password', 'password-confirm', messages={'invalid': u'Your passwords don\'t match.'})] } - def password_POST(self): - user = Session.query(User).get(c.form_values[u'user_id']) + def password_POST(self, id=None): + user = Session.query(User).get(id) if not user: h.flash.set_message(u'There was a problem with your account, please reset your password again.', u'error') h.redirect_to(h.url_for(controller=u'account', action=u'login')) user.password = utils.hash_password(c.form_values[u'password-password']) + user.activation_key = None user.modified = datetime.now() Session.add(user) Session.commit() diff --git a/scribeengine/templates/account/password.mako b/scribeengine/templates/account/password.mako index 12ccff5..3eaa03e 100644 --- a/scribeengine/templates/account/password.mako +++ b/scribeengine/templates/account/password.mako @@ -3,7 +3,7 @@

New password

<%include file="/errors.mako"/> -
+