From 9537d7f78b4ccc4b0261aa4d73a3c21e5b8692ca Mon Sep 17 00:00:00 2001 From: kelepirci Date: Fri, 5 Aug 2016 23:39:35 +0300 Subject: [PATCH] Change Email with Confirmation This commit will introduce change email feature with email confirmation system. --- dash/models.py | 22 ++++++++++++ dash/profile/forms.py | 5 +-- dash/profile/views.py | 29 +++++++++++++-- .../templates/profile/email/change_email.html | 9 +++++ dash/templates/profile/email/change_email.txt | 13 +++++++ dash/templates/profile/index.html | 35 +++++++++++++++++-- 6 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 dash/templates/profile/email/change_email.html create mode 100644 dash/templates/profile/email/change_email.txt diff --git a/dash/models.py b/dash/models.py index 2c8d66a..cd34292 100644 --- a/dash/models.py +++ b/dash/models.py @@ -80,6 +80,28 @@ class User(UserMixin, db.Model): self.password = new_password db.session.add(self) return True + + # generates token for email change + def generate_email_change_token(self, new_email, expiration=3600): + s = Serializer(current_app.config['SECRET_KEY'], expiration) + return s.dumps({'change_email': self.id, 'new_email': new_email}) + + def change_email(self, token): + s = Serializer(current_app.config['SECRET_KEY']) + try: + data = s.loads(token) + except: + return False + if data.get('change_email') != self.id: + return False + new_email = data.get('new_email') + if new_email is None: + return False + if self.query.filter_by(email=new_email).first() is not None: + return False + self.email = new_email + db.session.add(self) + return True def __repr__(self): return '' % self.username diff --git a/dash/profile/forms.py b/dash/profile/forms.py index 46b5f17..dfbcb9f 100644 --- a/dash/profile/forms.py +++ b/dash/profile/forms.py @@ -30,9 +30,10 @@ class ChangeUserNameForm(Form): if User.query.filter_by(username=field.data).first(): raise ValidationError('Username already in use.') -class UpdateEmailForm(Form): - email = StringField('Email', validators=[Required(), Length(1, 128), +class ChangeEmailForm(Form): + email = StringField('New Email', validators=[Required(), Length(1, 128), Email()]) + password = PasswordField('Password', validators=[Required()]) type = StringField() def validate_email(self, field): diff --git a/dash/profile/views.py b/dash/profile/views.py index 1ee052a..ee59f49 100644 --- a/dash/profile/views.py +++ b/dash/profile/views.py @@ -6,7 +6,8 @@ from . import profile from .. import db from ..models import User from ..email import send_email -from .forms import ChangePasswordForm, UpdateProfileForm, ChangeUserNameForm +from .forms import ChangePasswordForm, UpdateProfileForm, ChangeUserNameForm, \ + ChangeEmailForm @profile.route('/', methods=['GET', 'POST']) @@ -15,6 +16,7 @@ def index(): formChangePassword = ChangePasswordForm() formChangeUserName = ChangeUserNameForm() formUpdateProfile = UpdateProfileForm() + formChangeEmail = ChangeEmailForm() if formChangePassword.type.data == 'formChangePassword': if formChangePassword.validate_on_submit(): @@ -40,7 +42,30 @@ def index(): db.session.add(current_user) flash('formUpdateProfile') return redirect(url_for('profile.index')) + if formChangeEmail.type.data == 'formChangeEmail': + if formChangeEmail.validate_on_submit(): + if current_user.verify_password(formChangeEmail.password.data): + new_email = formChangeEmail.email.data + token = current_user.generate_email_change_token(new_email) + send_email(new_email, 'Confirm your email address', + 'profile/email/change_email', + user=current_user, token=token) + flash('An email with instructions to confirm your new email ' + 'address has been sent to you.') + return redirect(url_for('profile.index')) + else: + flash('Invalid email address or password.') return render_template('profile/index.html', formChangePassword=formChangePassword, formChangeUserName=formChangeUserName, - formUpdateProfile=formUpdateProfile) + formUpdateProfile=formUpdateProfile, + formChangeEmail=formChangeEmail) + +@profile.route('/change-email/') +@login_required +def change_email(token): + if current_user.change_email(token): + flash('Your email address has been updated.') + else: + flash('Invalid request.') + return redirect(url_for('profile.index')) diff --git a/dash/templates/profile/email/change_email.html b/dash/templates/profile/email/change_email.html new file mode 100644 index 0000000..b2da1e5 --- /dev/null +++ b/dash/templates/profile/email/change_email.html @@ -0,0 +1,9 @@ +

Dear {{ user.username }},

+

We have received a request to change your email.

+

To confirm your your new email click here.

+

Your new email is {{ new_email }}

+

Alternatively, you can paste the following link in your browser's address bar:

+

{{ url_for('profile.change_email', token=token, _external=True) }}

+

Sincerely,

+

The - Stack Team

+

Note: replies to this email address are not monitored.

\ No newline at end of file diff --git a/dash/templates/profile/email/change_email.txt b/dash/templates/profile/email/change_email.txt new file mode 100644 index 0000000..8cacfad --- /dev/null +++ b/dash/templates/profile/email/change_email.txt @@ -0,0 +1,13 @@ +Dear {{ user.username }}, + +We have received a request to change your email. + +To confirm your your new email click here. + +Alternatively, you can paste the following link in your browser's address bar: +{{ url_for('profile.change_email', token=token, _external=True) }} + +Sincerely, +The - Stack Team + +Note: replies to this email address are not monitored \ No newline at end of file diff --git a/dash/templates/profile/index.html b/dash/templates/profile/index.html index 5d859f8..b807188 100644 --- a/dash/templates/profile/index.html +++ b/dash/templates/profile/index.html @@ -344,7 +344,7 @@
- +
@@ -364,7 +364,7 @@
- +
@@ -383,10 +383,39 @@
- +
+
+

Change Your Email

+
+ + {{ formChangeEmail.hidden_tag() }} +
+
+ + + {% if formChangeEmail.email.errors %} + {% for error in formChangeEmail.email.errors %} {{ error }} {% endfor %} + {% endif %} +
+
+
+
+ + + {% if formChangeEmail.password.errors %} + {% for error in formChangeEmail.password.errors %} {{ error }} {% endfor %} + {% endif %} +
+
+
+
+ +
+
+