Email confirmation system added
User confirmation based on email
This commit is contained in:
parent
c8c5d627bd
commit
669eeaabad
@ -14,8 +14,8 @@ class Config:
|
|||||||
|
|
||||||
class DevelopmentConfig(Config):
|
class DevelopmentConfig(Config):
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
MAIL_SERVER = 'smtp.googlemail.com'
|
MAIL_SERVER = 'localhost'
|
||||||
MAIL_PORT = 587
|
MAIL_PORT = 25
|
||||||
MAIL_USE_TLS = True
|
MAIL_USE_TLS = True
|
||||||
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
|
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
|
||||||
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
|
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
|
||||||
|
@ -8,6 +8,19 @@ from ..models import User
|
|||||||
from ..email import send_email
|
from ..email import send_email
|
||||||
from .forms import LoginForm, RegistrationForm
|
from .forms import LoginForm, RegistrationForm
|
||||||
|
|
||||||
|
@auth.before_app_request
|
||||||
|
def before_request():
|
||||||
|
if current_user.is_authenticated \
|
||||||
|
and not current_user.confirmed \
|
||||||
|
and request.endpoint[:5] != 'auth.' \
|
||||||
|
and request.endpoint != 'static':
|
||||||
|
return redirect(url_for('auth.unconfirmed'))
|
||||||
|
|
||||||
|
@auth.route('/unconfirmed')
|
||||||
|
def unconfirmed():
|
||||||
|
if current_user.is_anonymous or current_user.confirmed:
|
||||||
|
return redirect(url_for('main.index'))
|
||||||
|
return render_template('auth/unconfirmed.html')
|
||||||
|
|
||||||
@auth.route('/login', methods=['GET', 'POST'])
|
@auth.route('/login', methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
@ -38,6 +51,32 @@ def register():
|
|||||||
avatar="/static/img/user2-160x160.jpg",
|
avatar="/static/img/user2-160x160.jpg",
|
||||||
created_at=datetime.datetime.now())
|
created_at=datetime.datetime.now())
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
flash('You can now login.')
|
db.session.commit()
|
||||||
return redirect(url_for('auth.login'))
|
token = user.generate_confirmation_token()
|
||||||
|
send_email(user.email, 'Confirm Your Account',
|
||||||
|
'auth/email/confirm', user=user, token=token)
|
||||||
|
flash('A confirmation email has been sent to you by email.')
|
||||||
|
return redirect(url_for('main.index'))
|
||||||
return render_template('auth/register.html', form=form)
|
return render_template('auth/register.html', form=form)
|
||||||
|
|
||||||
|
# user email confirmation function
|
||||||
|
@auth.route('/confirm/<token>')
|
||||||
|
@login_required
|
||||||
|
def confirm(token):
|
||||||
|
if current_user.confirmed:
|
||||||
|
return redirect(url_for('main.index'))
|
||||||
|
if current_user.confirm(token):
|
||||||
|
flash('You have confirmed your account. Thanks!')
|
||||||
|
else:
|
||||||
|
flash('The confirmation link is invalid or has expired.')
|
||||||
|
return redirect(url_for('main.index'))
|
||||||
|
|
||||||
|
# resend confirmation email
|
||||||
|
@auth.route('/confirm')
|
||||||
|
@login_required
|
||||||
|
def resend_confirmation():
|
||||||
|
token = current_user.generate_confirmation_token()
|
||||||
|
send_email(current_user.email, 'Confirm Your Account',
|
||||||
|
'auth/email/confirm', user=current_user, token=token)
|
||||||
|
flash('A new confirmation email has been sent to you by email.')
|
||||||
|
return redirect(url_for('main.index'))
|
@ -4,17 +4,17 @@ from flask_mail import Message
|
|||||||
from . import mail
|
from . import mail
|
||||||
|
|
||||||
|
|
||||||
def send_async_email(app, msg):
|
def send_async_email(dash, msg):
|
||||||
with app.app_context():
|
with dash.app_context():
|
||||||
mail.send(msg)
|
mail.send(msg)
|
||||||
|
|
||||||
|
|
||||||
def send_email(to, subject, template, **kwargs):
|
def send_email(to, subject, template, **kwargs):
|
||||||
app = current_app._get_current_object()
|
dash = current_app._get_current_object()
|
||||||
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
|
msg = Message(dash.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
|
||||||
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
|
sender=dash.config['FLASKY_MAIL_SENDER'], recipients=[to])
|
||||||
msg.body = render_template(template + '.txt', **kwargs)
|
msg.body = render_template(template + '.txt', **kwargs)
|
||||||
msg.html = render_template(template + '.html', **kwargs)
|
msg.html = render_template(template + '.html', **kwargs)
|
||||||
thr = Thread(target=send_async_email, args=[app, msg])
|
thr = Thread(target=send_async_email, args=[dash, msg])
|
||||||
thr.start()
|
thr.start()
|
||||||
return thr
|
return thr
|
@ -10,9 +10,6 @@ from flask_login import UserMixin
|
|||||||
from . import db
|
from . import db
|
||||||
from . import login_manager
|
from . import login_manager
|
||||||
|
|
||||||
@login_manager.user_loader
|
|
||||||
def load_user(user_id):
|
|
||||||
return User.query.get(int(user_id))
|
|
||||||
|
|
||||||
class Role(db.Model):
|
class Role(db.Model):
|
||||||
__tablename__ = 'roles'
|
__tablename__ = 'roles'
|
||||||
@ -49,11 +46,12 @@ class User(UserMixin, db.Model):
|
|||||||
def verify_password(self, password):
|
def verify_password(self, password):
|
||||||
return check_password_hash(self.password_hash, password)
|
return check_password_hash(self.password_hash, password)
|
||||||
|
|
||||||
# user email confirmation
|
# generates confirmation token for user email confirmation
|
||||||
def generate_confirmation_token(self, expiration=3600):
|
def generate_confirmation_token(self, expiration=3600):
|
||||||
s = Serializer(current_app.config['SECRET_KEY'], expiration)
|
s = Serializer(current_app.config['SECRET_KEY'], expiration)
|
||||||
return s.dumps({'confirm': self.id})
|
return s.dumps({'confirm': self.id})
|
||||||
|
|
||||||
|
# confirms uer email by id
|
||||||
def confirm(self, token):
|
def confirm(self, token):
|
||||||
s = Serializer(current_app.config['SECRET_KEY'])
|
s = Serializer(current_app.config['SECRET_KEY'])
|
||||||
try:
|
try:
|
||||||
@ -68,3 +66,7 @@ class User(UserMixin, db.Model):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User %r>' % self.username
|
return '<User %r>' % self.username
|
||||||
|
|
||||||
|
@login_manager.user_loader
|
||||||
|
def load_user(user_id):
|
||||||
|
return User.query.get(int(user_id))
|
8
dash/templates/auth/email/confirm.html
Normal file
8
dash/templates/auth/email/confirm.html
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<p>Dear {{ user.username }},</p>
|
||||||
|
<p>Welcome to <b>- Stack</b>!</p>
|
||||||
|
<p>To confirm your account please <a href="{{ url_for('auth.confirm', token=token, _external=True) }}">click here</a>.</p>
|
||||||
|
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
|
||||||
|
<p>{{ url_for('auth.confirm', token=token, _external=True) }}</p>
|
||||||
|
<p>Sincerely,</p>
|
||||||
|
<p>The - Stack Team</p>
|
||||||
|
<p><small>Note: replies to this email address are not monitored.</small></p>
|
11
dash/templates/auth/email/confirm.txt
Normal file
11
dash/templates/auth/email/confirm.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Dear {{ user.username }},
|
||||||
|
|
||||||
|
Welcome to - Stack!
|
||||||
|
|
||||||
|
To confirm your account please click on the following link:
|
||||||
|
|
||||||
|
{{ url_for('auth.confirm', token=token, _external=True) }}
|
||||||
|
|
||||||
|
Sincerely,
|
||||||
|
|
||||||
|
The - Stack Team
|
@ -27,21 +27,21 @@
|
|||||||
<form action="{{ url_for('auth.register') }}" method="post" name="register">
|
<form action="{{ url_for('auth.register') }}" method="post" name="register">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
<div class="form-group has-feedback">
|
<div class="form-group has-feedback">
|
||||||
<input type="email" name="email" class="form-control" placeholder="Email">
|
<input type="email" name="email" class="form-control" placeholder="Email" value="{{ request.form['email'] }}">
|
||||||
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
|
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
|
||||||
{% if form.email.errors %}
|
{% if form.email.errors %}
|
||||||
<span class="text-red">{% for error in form.email.errors %} {{ error }} {% endfor %}</span>
|
<span class="text-red">{% for error in form.email.errors %} {{ error }} {% endfor %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-feedback">
|
<div class="form-group has-feedback">
|
||||||
<input type="text" name="username" class="form-control" placeholder="User name">
|
<input type="text" name="username" class="form-control" placeholder="User name" value="{{ request.form['username'] }}">
|
||||||
<span class="glyphicon glyphicon-user form-control-feedback"></span>
|
<span class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||||
{% if form.username.errors %}
|
{% if form.username.errors %}
|
||||||
<span class="text-red">{% for error in form.username.errors %} {{ error }} {% endfor %}</span>
|
<span class="text-red">{% for error in form.username.errors %} {{ error }} {% endfor %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-feedback">
|
<div class="form-group has-feedback">
|
||||||
<input type="text" name="full_name" class="form-control" placeholder="Full name">
|
<input type="text" name="full_name" class="form-control" placeholder="Full name" value="{{ request.form['full_name'] }}">
|
||||||
<span class="glyphicon glyphicon-user form-control-feedback"></span>
|
<span class="glyphicon glyphicon-user form-control-feedback"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group has-feedback">
|
<div class="form-group has-feedback">
|
||||||
|
35
dash/templates/auth/unconfirmed.html
Normal file
35
dash/templates/auth/unconfirmed.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{% extends "adminlte/base_without_nav.html" %}
|
||||||
|
|
||||||
|
{% block title %}Confirm Account{% endblock %}
|
||||||
|
{% block description %}You have not confirmed your account!{% endblock %}
|
||||||
|
{% block bodytag %}login-page{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="login-logo">
|
||||||
|
<strong>-</strong> stack
|
||||||
|
</div>
|
||||||
|
<div class="login-box-body">
|
||||||
|
{# Display errors (if there are any). #}
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}
|
||||||
|
<ul>
|
||||||
|
{% for message in messages %}
|
||||||
|
<li>{{ message }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
<h3>Hello {{ current_user.username }}</h3>
|
||||||
|
<h4>You have not confirmed your account yet!</h4>
|
||||||
|
<p>Before accessing this page, you need to confirm your account.
|
||||||
|
Please check your email inbox and click on confirmation link.</p>
|
||||||
|
<p>If you have not received confirmation email:</p>
|
||||||
|
<a href="{{ url_for('auth.resend_confirmation') }}">
|
||||||
|
<button type="button" class="btn btn-primary">Re-Send Confirmation</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user