diff --git a/app/__init__.py b/app/__init__.py index 24218cb..c2ed101 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -3,6 +3,7 @@ from flask_bootstrap import Bootstrap from flask_mail import Mail from flask_moment import Moment from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager from config import config @@ -12,6 +13,10 @@ moment = Moment() db = SQLAlchemy() +login_manager = LoginManager() +login_manager.login_view = 'auth.login' + + def create_app(config_name): app = Flask(__name__) app.config.from_object(config[config_name]) @@ -21,6 +26,7 @@ def create_app(config_name): mail.init_app(app) moment.init_app(app) db.init_app(app) + login_manager.init_app(app) from .main import main as main_blueprint app.register_blueprint(main_blueprint) diff --git a/app/auth/forms.py b/app/auth/forms.py new file mode 100644 index 0000000..dad117d --- /dev/null +++ b/app/auth/forms.py @@ -0,0 +1,11 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, BooleanField, SubmitField +from wtforms.validators import DataRequired, Length, Email + + +class LoginForm(FlaskForm): + email = StringField('Email', validators=[DataRequired(), Length(1, 64), + Email()]) + password = PasswordField('Password', validators=[DataRequired()]) + remember_me = BooleanField('Keep me logged in') + submit = SubmitField('Log in') diff --git a/app/auth/views.py b/app/auth/views.py index 50109e0..07c0ffe 100644 --- a/app/auth/views.py +++ b/app/auth/views.py @@ -1,7 +1,28 @@ -from flask import render_template +from flask import render_template, redirect, request, url_for, flash +from flask_login import login_user, logout_user, login_required from . import auth +from ..models import User +from .forms import LoginForm -@auth.route('/login') +@auth.route('/login', methods=['GET', 'POST']) def login(): - return render_template('auth/login.html') + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(email=form.email.data).first() + if user is not None and user.verify_password(form.password.data): + login_user(user, form.remember_me.data) + next = request.args.get('next') + if next is None or not next.startswith('/'): + next = url_for('main.index') + return redirect(next) + flash('Invalid username or password') + return render_template('auth/login.html', form=form) + + +@auth.route('/logout') +@login_required +def logout(): + logout_user() + flash('You have been logged out.') + return redirect(url_for('main.index')) diff --git a/app/main/views.py b/app/main/views.py index b8a380e..171289c 100644 --- a/app/main/views.py +++ b/app/main/views.py @@ -1,29 +1,7 @@ -from flask import render_template, session, redirect, url_for, current_app -from .. import db -from ..models import User -from ..email import send_email +from flask import render_template from . import main -from .forms import NameForm @main.route('/', methods=['GET', 'POST']) def index(): - form = NameForm() - if form.validate_on_submit(): - user = User.query.filter_by(username=form.name.data).first() - if user is None: - user = User(username=form.name.data) - db.session.add(user) - db.session.commit() - session['known'] = False - if current_app.config['FLASKY_ADMIN']: - send_email(current_app.config['FLASKY_ADMIN'], 'New user', - 'mail/new_user', user=user) - else: - session['known'] = True - session['name'] = form.name.data - form.name.data = '' - return redirect(url_for('.index')) - return render_template('index.html', - form=form, name=session.get('name'), - known=session.get('known', False)) + return render_template('index.html') diff --git a/app/models.py b/app/models.py index 063c42b..6d69b5a 100644 --- a/app/models.py +++ b/app/models.py @@ -1,5 +1,6 @@ from werkzeug.security import generate_password_hash, check_password_hash -from . import db +from flask_login import UserMixin +from . import db, login_manager class Role(db.Model): @@ -12,9 +13,10 @@ class Role(db.Model): return '' % self.name -class User(db.Model): +class User(UserMixin, db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) + email = db.Column(db.String(64), unique=True, index=True) username = db.Column(db.String(64), unique=True, index=True) role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) password_hash = db.Column(db.String(128)) @@ -32,3 +34,8 @@ class User(db.Model): def __repr__(self): return '' % self.username + + +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html index 50a1351..476cff5 100644 --- a/app/templates/auth/login.html +++ b/app/templates/auth/login.html @@ -1,4 +1,5 @@ {% extends "base.html" %} +{% import "bootstrap/wtf.html" as wtf %} {% block title %}Flasky - Login{% endblock %} @@ -6,4 +7,7 @@ +
+ {{ wtf.quick_form(form) }} +
{% endblock %} diff --git a/app/templates/base.html b/app/templates/base.html index 1119f07..9851a28 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -25,6 +25,13 @@ + diff --git a/app/templates/index.html b/app/templates/index.html index 5479c64..6333217 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,16 +1,9 @@ {% extends "base.html" %} -{% import "bootstrap/wtf.html" as wtf %} {% block title %}Flasky{% endblock %} {% block page_content %} -{{ wtf.quick_form(form) }} {% endblock %} \ No newline at end of file diff --git a/migrations/versions/655013143dbb_.py b/migrations/versions/655013143dbb_.py new file mode 100644 index 0000000..ea6645c --- /dev/null +++ b/migrations/versions/655013143dbb_.py @@ -0,0 +1,30 @@ +"""empty message + +Revision ID: 655013143dbb +Revises: e7148b675688 +Create Date: 2018-11-01 23:41:21.492859 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '655013143dbb' +down_revision = 'e7148b675688' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('email', sa.String(length=64), nullable=True)) + op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_users_email'), table_name='users') + op.drop_column('users', 'email') + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index 27efa21..b60a12e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ Click==7.0 dominate==2.3.4 Flask==1.0.2 Flask-Bootstrap==3.3.7.1 +Flask-Login==0.4.1 Flask-Mail==0.9.1 Flask-Migrate==2.2.1 Flask-Moment==0.6.0