From c2ad766641020930478917879d39ba61908c6fa1 Mon Sep 17 00:00:00 2001 From: Cédric Bonhomme Date: Mon, 16 Jun 2014 08:03:58 +0200 Subject: Enables the user to recover its account when he has forgotten its password. --- pyaggr3g470r/emails.py | 35 +++++++++++++++++++++++++++++------ pyaggr3g470r/forms.py | 22 ++++++++++++++++++++++ pyaggr3g470r/templates/login.html | 2 ++ pyaggr3g470r/templates/recover.html | 22 ++++++++++++++++++++++ pyaggr3g470r/views.py | 34 +++++++++++++++++++++++++++++++++- 5 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 pyaggr3g470r/templates/recover.html diff --git a/pyaggr3g470r/emails.py b/pyaggr3g470r/emails.py index ae73c0e1..aba0e3e6 100644 --- a/pyaggr3g470r/emails.py +++ b/pyaggr3g470r/emails.py @@ -1,6 +1,24 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- +# pyAggr3g470r - A Web based news aggregator. +# Copyright (C) 2010-2014 Cédric Bonhomme - http://cedricbonhomme.org/ +# +# For more information : https://bitbucket.org/cedricbonhomme/pyaggr3g470r/ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + import logging import smtplib from email.mime.multipart import MIMEMultipart @@ -14,7 +32,6 @@ from decorators import async logger = logging.getLogger(__name__) - @async def send_async_email(mfrom, mto, msg): try: @@ -26,7 +43,6 @@ def send_async_email(mfrom, mto, msg): s.sendmail(mfrom, mto, msg.as_string()) s.quit() - def send_email(mfrom, mto, feed, article): """ Send the article via mail. @@ -62,7 +78,6 @@ def send_email(mfrom, mto, feed, article): s.quit() - # # Notifications # @@ -85,7 +100,6 @@ def send_heroku(user=None, bcc="", subject="", plaintext=""): logger.exception("send_heroku raised:") raise e - def information_message(subject, plaintext): """ Send an information message to the users of the platform. @@ -105,7 +119,6 @@ def information_message(subject, plaintext): else: pass - def new_account_notification(user): """ Account creation notification. @@ -117,7 +130,6 @@ def new_account_notification(user): else: pass - def new_account_activation(user): """ Account activation notification. @@ -129,6 +141,17 @@ def new_account_activation(user): else: pass +def new_password_notification(user, password): + """ + """ + plaintext = """Hello,\n\nA new password has been generated at your request:\n\n%s""" % \ + (password, ) + plaintext += "\n\nIt is advised to replace it as soon as connected to pyAggr3g470r.\n\nSee you," + + if conf.ON_HEROKU: + send_heroku(user=user, subject="[pyAggr3g470r] New password", plaintext=plaintext) + else: + pass def new_article_notification(user, feed, article): if conf.ON_HEROKU: diff --git a/pyaggr3g470r/forms.py b/pyaggr3g470r/forms.py index 5e977071..e35d8b2a 100644 --- a/pyaggr3g470r/forms.py +++ b/pyaggr3g470r/forms.py @@ -116,3 +116,25 @@ class InformationMessageForm(Form): subject = TextField(lazy_gettext("Subject"), [validators.Required(lazy_gettext("Please enter a subject."))]) message = TextAreaField(lazy_gettext("Message"), [validators.Required(lazy_gettext("Please enter a content."))]) submit = SubmitField(lazy_gettext("Send")) + +class RecoverPasswordForm(Form): + email = EmailField("Email", [validators.Length(min=6, max=35), validators.Required(lazy_gettext("Please enter your email address."))]) + submit = SubmitField(lazy_gettext("Save")) + + def __init__(self, *args, **kwargs): + Form.__init__(self, *args, **kwargs) + + def validate(self): + if not Form.validate(self): + return False + + user = User.query.filter(User.email == self.email.data).first() + if user and user.activation_key == "": + return True + elif user and user.activation_key != "": + flash(lazy_gettext('Account not confirmed.'), 'danger') + return False + else: + flash(lazy_gettext('Invalid email.'), 'danger') + #self.email.errors.append("Invalid email") + return False diff --git a/pyaggr3g470r/templates/login.html b/pyaggr3g470r/templates/login.html index 0a3bec2e..e58b5d83 100644 --- a/pyaggr3g470r/templates/login.html +++ b/pyaggr3g470r/templates/login.html @@ -27,5 +27,7 @@ {{ _('Sign up') }} +   + {{ _('Forgot password') }} {% endblock %} \ No newline at end of file diff --git a/pyaggr3g470r/templates/recover.html b/pyaggr3g470r/templates/recover.html new file mode 100644 index 00000000..8e690bdb --- /dev/null +++ b/pyaggr3g470r/templates/recover.html @@ -0,0 +1,22 @@ +{% extends "layout.html" %} +{% block content %} +
+
+

{{ _('Recover your account') }}

+ + {% for message in form.email.errors %} +
{{ message }}
+ {% endfor %} + +
+ {{ form.hidden_tag() }} + +
+ {{ form.email(class_="form-control", placeholder=_('Your email')) }} +
+ + {{ form.submit(class_="btn") }} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/pyaggr3g470r/views.py b/pyaggr3g470r/views.py index 178188d6..daf1d84e 100644 --- a/pyaggr3g470r/views.py +++ b/pyaggr3g470r/views.py @@ -46,7 +46,8 @@ import export import emails if not conf.ON_HEROKU: import search as fastsearch -from forms import SignupForm, SigninForm, AddFeedForm, ProfileForm, InformationMessageForm +from forms import SignupForm, SigninForm, AddFeedForm, \ + ProfileForm, InformationMessageForm, RecoverPasswordForm from pyaggr3g470r import app, db, allowed_file, babel from pyaggr3g470r.models import User, Feed, Article, Role from pyaggr3g470r.decorators import feed_access_required @@ -740,6 +741,37 @@ def confirm_account(activation_key=None): flash(gettext('Impossible to confirm this account.'), 'danger') return redirect(url_for('login')) +@app.route('/recover', methods=['GET', 'POST']) +def recover(): + """ + Enables the user to recover its account when he has forgotten + its password. + """ + import string + import random + form = RecoverPasswordForm() + + if request.method == 'POST': + if form.validate(): + user = User.query.filter(User.email == form.email.data).first() + characters = string.ascii_letters + string.digits + password = "".join(random.choice(characters) for x in range(random.randint(8, 16))) + user.set_password(password) + db.session.commit() + + # Send the confirmation email + try: + emails.new_password_notification(user, password) + flash(gettext('New password sent to your address.'), 'success') + except Exception as e: + flash(gettext('Problem while sending your new password.') + ': ' + str(e), 'danger') + + return redirect(url_for('login')) + return render_template('recover.html', form=form) + + if request.method == 'GET': + return render_template('recover.html', form=form) + # # Views dedicated to administration tasks. # -- cgit