mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-12-25 02:44:18 +00:00
Rework auth package
This commit is contained in:
parent
c90cd4d8a3
commit
3b798cb617
@ -10,7 +10,7 @@ import logging
|
|||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
login_manager.login_view = 'main.index'
|
login_manager.login_view = 'auth.login'
|
||||||
mail = Mail()
|
mail = Mail()
|
||||||
socketio = SocketIO()
|
socketio = SocketIO()
|
||||||
|
|
||||||
|
@ -12,25 +12,6 @@ class LoginForm(FlaskForm):
|
|||||||
submit = SubmitField('Log In')
|
submit = SubmitField('Log In')
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetForm(FlaskForm):
|
|
||||||
password = PasswordField(
|
|
||||||
'New Password',
|
|
||||||
validators=[DataRequired(),
|
|
||||||
EqualTo('password2', message='Passwords must match')]
|
|
||||||
)
|
|
||||||
password2 = PasswordField(
|
|
||||||
'Confirm password',
|
|
||||||
validators=[DataRequired(),
|
|
||||||
EqualTo('password', message='Passwords must match.')]
|
|
||||||
)
|
|
||||||
submit = SubmitField('Reset Password')
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetRequestForm(FlaskForm):
|
|
||||||
email = StringField('Email', validators=[DataRequired(), Email()])
|
|
||||||
submit = SubmitField('Reset Password')
|
|
||||||
|
|
||||||
|
|
||||||
class RegistrationForm(FlaskForm):
|
class RegistrationForm(FlaskForm):
|
||||||
email = StringField('Email', validators=[DataRequired(), Email()])
|
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||||
username = StringField(
|
username = StringField(
|
||||||
@ -55,3 +36,22 @@ class RegistrationForm(FlaskForm):
|
|||||||
def validate_username(self, field):
|
def validate_username(self, field):
|
||||||
if User.query.filter_by(username=field.data).first():
|
if User.query.filter_by(username=field.data).first():
|
||||||
raise ValidationError('Username already in use.')
|
raise ValidationError('Username already in use.')
|
||||||
|
|
||||||
|
|
||||||
|
class ResetPasswordForm(FlaskForm):
|
||||||
|
password = PasswordField(
|
||||||
|
'New Password',
|
||||||
|
validators=[DataRequired(),
|
||||||
|
EqualTo('password2', message='Passwords must match')]
|
||||||
|
)
|
||||||
|
password2 = PasswordField(
|
||||||
|
'Confirm password',
|
||||||
|
validators=[DataRequired(),
|
||||||
|
EqualTo('password', message='Passwords must match.')]
|
||||||
|
)
|
||||||
|
submit = SubmitField('Reset Password')
|
||||||
|
|
||||||
|
|
||||||
|
class ResetPasswordRequestForm(FlaskForm):
|
||||||
|
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||||
|
submit = SubmitField('Reset Password')
|
||||||
|
@ -1,62 +1,14 @@
|
|||||||
from app import db
|
from app import db, logger
|
||||||
from app.email import send_email
|
from app.email import send_email
|
||||||
from app.models import User
|
from app.models import User
|
||||||
from flask import (current_app, flash, redirect, render_template, request,
|
from flask import (current_app, flash, redirect, render_template, request,
|
||||||
url_for)
|
url_for)
|
||||||
from flask_login import current_user, login_required, logout_user
|
from flask_login import current_user, login_user, login_required, logout_user
|
||||||
from . import auth
|
from . import auth
|
||||||
from .forms import (PasswordResetForm, PasswordResetRequestForm,
|
from .forms import (LoginForm, ResetPasswordForm, ResetPasswordRequestForm,
|
||||||
RegistrationForm)
|
RegistrationForm)
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
@auth.route('/logout')
|
|
||||||
@login_required
|
|
||||||
def logout():
|
|
||||||
logout_user()
|
|
||||||
flash('You have been logged out.')
|
|
||||||
return redirect(url_for('main.index'))
|
|
||||||
|
|
||||||
|
|
||||||
@auth.route('/register', methods=['GET', 'POST'])
|
|
||||||
def register():
|
|
||||||
if not current_user.is_anonymous:
|
|
||||||
return redirect(url_for('main.dashboard'))
|
|
||||||
registration_form = RegistrationForm(prefix='registration-form')
|
|
||||||
if registration_form.validate_on_submit():
|
|
||||||
user = User(email=registration_form.email.data.lower(),
|
|
||||||
password=registration_form.password.data,
|
|
||||||
username=registration_form.username.data)
|
|
||||||
db.session.add(user)
|
|
||||||
db.session.commit()
|
|
||||||
dir = os.path.join(current_app.config['NOPAQUE_STORAGE'], str(user.id))
|
|
||||||
try:
|
|
||||||
os.makedirs(dir)
|
|
||||||
except OSError:
|
|
||||||
flash('[ERROR]!')
|
|
||||||
user.delete()
|
|
||||||
else:
|
|
||||||
token = user.generate_confirmation_token()
|
|
||||||
send_email(user.email, 'Confirm Your Account',
|
|
||||||
'auth/email/confirm', token=token, user=user)
|
|
||||||
flash('A confirmation email has been sent to you by email.')
|
|
||||||
return redirect(url_for('main.index'))
|
|
||||||
return render_template('auth/register.html.j2',
|
|
||||||
registration_form=registration_form,
|
|
||||||
title='Register')
|
|
||||||
|
|
||||||
|
|
||||||
@auth.route('/confirm/<token>')
|
|
||||||
@login_required
|
|
||||||
def confirm(token):
|
|
||||||
if current_user.confirmed:
|
|
||||||
return redirect(url_for('main.index'))
|
|
||||||
if current_user.confirm(token):
|
|
||||||
db.session.commit()
|
|
||||||
flash('You have confirmed your account. Thanks!')
|
|
||||||
else:
|
|
||||||
flash('The confirmation link is invalid or has expired.')
|
|
||||||
return redirect(url_for('main.dashboard'))
|
|
||||||
|
|
||||||
|
|
||||||
@auth.before_app_request
|
@auth.before_app_request
|
||||||
@ -72,10 +24,76 @@ def before_request():
|
|||||||
return redirect(url_for('auth.unconfirmed'))
|
return redirect(url_for('auth.unconfirmed'))
|
||||||
|
|
||||||
|
|
||||||
|
@auth.route('/login')
|
||||||
|
def login():
|
||||||
|
login_form = LoginForm(prefix='login-form')
|
||||||
|
if login_form.validate_on_submit():
|
||||||
|
user = User.query.filter_by(username=login_form.user.data).first()
|
||||||
|
if user is not None and user.verify_password(login_form.password.data):
|
||||||
|
login_user(user, login_form.remember_me.data)
|
||||||
|
next = request.args.get('next')
|
||||||
|
if next is None or not next.startswith('/'):
|
||||||
|
next = url_for('main.dashboard')
|
||||||
|
return redirect(next)
|
||||||
|
flash('Invalid username or password.')
|
||||||
|
return render_template('auth/login.html.j2', login_form=login_form,
|
||||||
|
title='nopaque')
|
||||||
|
|
||||||
|
|
||||||
|
@auth.route('/logout')
|
||||||
|
@login_required
|
||||||
|
def logout():
|
||||||
|
logout_user()
|
||||||
|
flash('You have been logged out.')
|
||||||
|
return redirect(url_for('main.index'))
|
||||||
|
|
||||||
|
|
||||||
|
@auth.route('/register', methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
if current_user.is_authenticated:
|
||||||
|
return redirect(url_for('main.dashboard'))
|
||||||
|
registration_form = RegistrationForm(prefix='registration-form')
|
||||||
|
if registration_form.validate_on_submit():
|
||||||
|
user = User(email=registration_form.email.data.lower(),
|
||||||
|
password=registration_form.password.data,
|
||||||
|
username=registration_form.username.data)
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
user_dir = os.path.join(current_app.config['NOPAQUE_STORAGE'],
|
||||||
|
str(user.id))
|
||||||
|
if os.path.exists(user_dir):
|
||||||
|
shutil.rmtree(user_dir)
|
||||||
|
os.mkdir(user_dir)
|
||||||
|
token = user.generate_confirmation_token()
|
||||||
|
send_email(user.email, 'Confirm Your Account',
|
||||||
|
'auth/email/confirm', token=token, user=user)
|
||||||
|
flash('A confirmation email has been sent to you by email.')
|
||||||
|
return redirect(url_for('auth.login'))
|
||||||
|
return render_template('auth/register.html.j2',
|
||||||
|
registration_form=registration_form,
|
||||||
|
title='Register')
|
||||||
|
|
||||||
|
|
||||||
|
@auth.route('/confirm/<token>')
|
||||||
|
@login_required
|
||||||
|
def confirm(token):
|
||||||
|
if current_user.confirmed:
|
||||||
|
return redirect(url_for('main.dashboard'))
|
||||||
|
if current_user.confirm(token):
|
||||||
|
db.session.commit()
|
||||||
|
flash('You have confirmed your account. Thanks!')
|
||||||
|
return redirect(url_for('main.dashboard'))
|
||||||
|
else:
|
||||||
|
flash('The confirmation link is invalid or has expired.')
|
||||||
|
return redirect(url_for('auth.unconfirmed'))
|
||||||
|
|
||||||
|
|
||||||
@auth.route('/unconfirmed')
|
@auth.route('/unconfirmed')
|
||||||
def unconfirmed():
|
def unconfirmed():
|
||||||
if current_user.is_anonymous or current_user.confirmed:
|
if current_user.is_anonymous:
|
||||||
return redirect(url_for('main.index'))
|
return redirect(url_for('main.index'))
|
||||||
|
elif current_user.confirmed:
|
||||||
|
return redirect(url_for('main.dashboard'))
|
||||||
return render_template('auth/unconfirmed.html.j2', title='Unconfirmed')
|
return render_template('auth/unconfirmed.html.j2', title='Unconfirmed')
|
||||||
|
|
||||||
|
|
||||||
@ -83,49 +101,46 @@ def unconfirmed():
|
|||||||
@login_required
|
@login_required
|
||||||
def resend_confirmation():
|
def resend_confirmation():
|
||||||
token = current_user.generate_confirmation_token()
|
token = current_user.generate_confirmation_token()
|
||||||
send_email(current_user.email,
|
send_email(current_user.email, 'Confirm Your Account',
|
||||||
'Confirm Your Account',
|
'auth/email/confirm', token=token, user=current_user)
|
||||||
'auth/email/confirm',
|
|
||||||
token=token,
|
|
||||||
user=current_user)
|
|
||||||
flash('A new confirmation email has benn sent to you by email.')
|
flash('A new confirmation email has benn sent to you by email.')
|
||||||
return redirect(url_for('main.dashboard'))
|
return redirect(url_for('auth.unconfirmed'))
|
||||||
|
|
||||||
|
|
||||||
@auth.route('/reset', methods=['GET', 'POST'])
|
@auth.route('/reset', methods=['GET', 'POST'])
|
||||||
def password_reset_request():
|
def reset_password_request():
|
||||||
if not current_user.is_anonymous:
|
if current_user.is_authenticated:
|
||||||
return redirect(url_for('main.dashboard'))
|
return redirect(url_for('main.dashboard'))
|
||||||
form = PasswordResetRequestForm()
|
reset_password_request_form = ResetPasswordRequestForm()
|
||||||
if form.validate_on_submit():
|
if reset_password_request_form.validate_on_submit():
|
||||||
user = User.query.filter_by(email=form.email.data.lower()).first()
|
submitted_email = reset_password_request_form.email.data
|
||||||
|
user = User.query.filter_by(email=submitted_email.lower()).first()
|
||||||
if user:
|
if user:
|
||||||
token = user.generate_reset_token()
|
token = user.generate_reset_token()
|
||||||
send_email(user.email,
|
send_email(user.email, 'Reset Your Password',
|
||||||
'Reset Your Password',
|
'auth/email/reset_password', token=token, user=user)
|
||||||
'auth/email/reset_password',
|
|
||||||
token=token,
|
|
||||||
user=user)
|
|
||||||
flash('An email with instructions to reset your password has been '
|
flash('An email with instructions to reset your password has been '
|
||||||
'sent to you.')
|
'sent to you.')
|
||||||
return redirect(url_for('main.index'))
|
return redirect(url_for('auth.login'))
|
||||||
return render_template('auth/reset_password_request.html.j2',
|
return render_template(
|
||||||
form=form,
|
'auth/reset_password_request.html.j2',
|
||||||
title='Password Reset')
|
reset_password_request_form=reset_password_request_form,
|
||||||
|
title='Password Reset'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@auth.route('/reset/<token>', methods=['GET', 'POST'])
|
@auth.route('/reset/<token>', methods=['GET', 'POST'])
|
||||||
def password_reset(token):
|
def reset_password(token):
|
||||||
if not current_user.is_anonymous:
|
if current_user.is_authenticated:
|
||||||
return redirect(url_for('main.dashboard'))
|
return redirect(url_for('main.dashboard'))
|
||||||
form = PasswordResetForm()
|
reset_password_form = ResetPasswordForm()
|
||||||
if form.validate_on_submit():
|
if reset_password_form.validate_on_submit():
|
||||||
if User.reset_password(token, form.password.data):
|
if User.reset_password(token, reset_password_form.password.data):
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash('Your password has been updated.')
|
flash('Your password has been updated.')
|
||||||
return redirect(url_for('main.index'))
|
return redirect(url_for('auth.login'))
|
||||||
else:
|
else:
|
||||||
return redirect(url_for('main.index'))
|
return redirect(url_for('main.index'))
|
||||||
return render_template('auth/reset_password.html.j2',
|
return render_template('auth/reset_password.html.j2',
|
||||||
form=form,
|
reset_password_form=reset_password_form,
|
||||||
title='Password Reset')
|
title='Password Reset')
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<p>Dear {{ user.username }},</p>
|
<p>Dear {{ user.username }},</p>
|
||||||
<p>To reset your password <a href="{{ url_for('auth.password_reset', token=token, _external=True) }}">click here</a>.</p>
|
<p>To reset your password <a href="{{ url_for('auth.reset_password', token=token, _external=True) }}">click here</a>.</p>
|
||||||
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
|
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
|
||||||
<p>{{ url_for('auth.password_reset', token=token, _external=True) }}</p>
|
<p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p>
|
||||||
<p>If you have not requested a password reset simply ignore this message.</p>
|
<p>If you have not requested a password reset simply ignore this message.</p>
|
||||||
<p>Sincerely,</p>
|
<p>Sincerely,</p>
|
||||||
<p>The nopaque Team</p>
|
<p>The nopaque Team</p>
|
||||||
|
@ -2,7 +2,7 @@ Dear {{ user.username }},
|
|||||||
|
|
||||||
To reset your password click on the following link:
|
To reset your password click on the following link:
|
||||||
|
|
||||||
{{ url_for('auth.password_reset', token=token, _external=True) }}
|
{{ url_for('auth.reset_password', token=token, _external=True) }}
|
||||||
|
|
||||||
If you have not requested a password reset simply ignore this message.
|
If you have not requested a password reset simply ignore this message.
|
||||||
|
|
||||||
|
52
app/templates/auth/login.html.j2
Normal file
52
app/templates/auth/login.html.j2
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{% extends "nopaque.html.j2" %}
|
||||||
|
|
||||||
|
{% block page_content %}
|
||||||
|
<div class="col s12 m4">
|
||||||
|
<h3>Cyber cyber</h3>
|
||||||
|
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col s12 m8">
|
||||||
|
<div class="card">
|
||||||
|
<form method="POST">
|
||||||
|
<div class="card-content">
|
||||||
|
<span class="card-title">Log in</span>
|
||||||
|
{{ login_form.hidden_tag() }}
|
||||||
|
<div class="input-field">
|
||||||
|
<i class="material-icons prefix">person</i>
|
||||||
|
{{ login_form.user(class='validate') }}
|
||||||
|
{{ login_form.user.label }}
|
||||||
|
{% for error in login_form.user.errors %}
|
||||||
|
<span class="helper-text red-text">{{ error }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="input-field">
|
||||||
|
<i class="material-icons prefix">vpn_key</i>
|
||||||
|
{{ login_form.password(class='validate') }}
|
||||||
|
{{ login_form.password.label }}
|
||||||
|
{% for error in login_form.password.errors %}
|
||||||
|
<span class="helper-text red-text">{{ error }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="row" style="margin-bottom: 0;">
|
||||||
|
<div class="col s6 left-align">
|
||||||
|
<a href="{{ url_for('auth.reset_password_request') }}">Forgot your password?</a>
|
||||||
|
</div>
|
||||||
|
<div class="col s6 right-align">
|
||||||
|
<div class="switch">
|
||||||
|
<label>
|
||||||
|
Remember me
|
||||||
|
{{ login_form.remember_me() }}
|
||||||
|
<span class="lever"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-action right-align">
|
||||||
|
{{ login_form.submit(class='btn') }}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -7,27 +7,27 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col s12 m8">
|
<div class="col s12 m8">
|
||||||
<div class="card small">
|
<div class="card">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
{{ form.hidden_tag() }}
|
{{ reset_password_form.hidden_tag() }}
|
||||||
<div class="input-field">
|
<div class="input-field">
|
||||||
{{ form.password(class='validate', type='password') }}
|
{{ reset_password_form.password(type='password') }}
|
||||||
{{ form.password.label }}
|
{{ reset_password_form.password.label }}
|
||||||
{% for error in form.password.errors %}
|
{% for error in reset_password_form.password.errors %}
|
||||||
<span class="helper-text red-text">{{ error }}</span>
|
<span class="helper-text red-text">{{ error }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="input-field">
|
<div class="input-field">
|
||||||
{{ form.password2(class='validate', type='password') }}
|
{{ reset_password_form.password2(type='password') }}
|
||||||
{{ form.password2.label }}
|
{{ reset_password_form.password2.label }}
|
||||||
{% for error in form.password2.errors %}
|
{% for error in reset_password_form.password2.errors %}
|
||||||
<span class="helper-text red-text">{{ error }}</span>
|
<span class="helper-text red-text">{{ error }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action right-align">
|
||||||
{{ form.submit(class='btn right') }}
|
{{ form.submit(class='btn') }}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,20 +7,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col s12 m8">
|
<div class="col s12 m8">
|
||||||
<div class="card small">
|
<div class="card">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
{{ form.hidden_tag() }}
|
{{ reset_password_request_form.hidden_tag() }}
|
||||||
<div class="input-field">
|
<div class="input-field">
|
||||||
{{ form.email(class='validate', type='email') }}
|
{{ reset_password_request_form.email(class='validate', type='email') }}
|
||||||
{{ form.email.label }}
|
{{ reset_password_request_form.email.label }}
|
||||||
{% for error in form.email.errors %}
|
{% for error in reset_password_request_form.email.errors %}
|
||||||
<span class="helper-text red-text">{{ error }}</span>
|
<span class="helper-text red-text">{{ error }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action right-align">
|
||||||
{{ form.submit(class='btn right') }}
|
{{ reset_password_request_form.submit(class='btn') }}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -116,7 +116,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row" style="margin-bottom: 0;">
|
<div class="row" style="margin-bottom: 0;">
|
||||||
<div class="col s6 left-align">
|
<div class="col s6 left-align">
|
||||||
<a href="{{ url_for('auth.password_reset_request') }}">Forgot your password?</a>
|
<a href="{{ url_for('auth.reset_password_request') }}">Forgot your password?</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col s6 right-align">
|
<div class="col s6 right-align">
|
||||||
<div class="switch">
|
<div class="switch">
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<li><a href="{{ url_for('profile.settings') }}"><i class="material-icons">settings</i>Settings</a></li>
|
<li><a href="{{ url_for('profile.settings') }}"><i class="material-icons">settings</i>Settings</a></li>
|
||||||
<li><a href="{{ url_for('auth.logout') }}"><i class="material-icons">power_settings_new</i>Log out</a></li>
|
<li><a href="{{ url_for('auth.logout') }}"><i class="material-icons">power_settings_new</i>Log out</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="{{ url_for('main.index', _anchor='registration-and-log-in') }}"><i class="material-icons">person</i>Log in</a></li>
|
<li><a href="{{ url_for('auth.login') }}"><i class="material-icons">person</i>Log in</a></li>
|
||||||
<li><a href="{{ url_for('auth.register') }}"><i class="material-icons">person_add</i>Register</a></li>
|
<li><a href="{{ url_for('auth.register') }}"><i class="material-icons">person_add</i>Register</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
@ -77,7 +77,7 @@
|
|||||||
<li><a href="{{ url_for('profile.settings') }}"><i class="material-icons">settings</i>Settings</a></li>
|
<li><a href="{{ url_for('profile.settings') }}"><i class="material-icons">settings</i>Settings</a></li>
|
||||||
<li><a href="{{ url_for('auth.logout') }}"><i class="material-icons">power_settings_new</i>Log out</a></li>
|
<li><a href="{{ url_for('auth.logout') }}"><i class="material-icons">power_settings_new</i>Log out</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="{{ url_for('main.index') }}"><i class="material-icons">person</i>Log in</a></li>
|
<li><a href="{{ url_for('auth.login') }}"><i class="material-icons">person</i>Log in</a></li>
|
||||||
<li><a href="{{ url_for('auth.register') }}"><i class="material-icons">person_add</i>Register</a></li>
|
<li><a href="{{ url_for('auth.register') }}"><i class="material-icons">person_add</i>Register</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if current_user.is_administrator() %}
|
{% if current_user.is_administrator() %}
|
||||||
|
Loading…
Reference in New Issue
Block a user