Flask-Login support

master
Steven Loria 11 years ago
parent fadc114ac7
commit 581ce4d981
  1. 1
      {{cookiecutter.repo_name}}/requirements/prod.txt
  2. 5
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py
  3. 3
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/extensions.py
  4. 37
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/public/forms.py
  5. 36
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/public/views.py
  6. 4
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/templates/_layouts/nav.html
  7. 36
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/user/forms.py
  8. 10
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/user/models.py
  9. 4
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/user/views.py
  10. 16
      {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/utils.py

@ -25,4 +25,5 @@ cssmin>=0.1.4
jsmin>=2.0.4 jsmin>=2.0.4
# Auth # Auth
Flask-Login>=0.2.7
Flask-Bcrypt>=0.5.2 Flask-Bcrypt>=0.5.2

@ -5,6 +5,7 @@ from webassets.loaders import PythonLoader
from {{ cookiecutter.repo_name }}.settings import ProdConfig from {{ cookiecutter.repo_name }}.settings import ProdConfig
from {{cookiecutter.repo_name}}.assets import assets from {{cookiecutter.repo_name}}.assets import assets
from {{cookiecutter.repo_name}}.extensions import login_manager
from {{cookiecutter.repo_name}}.database import db from {{cookiecutter.repo_name}}.database import db
from {{cookiecutter.repo_name}} import public, user from {{cookiecutter.repo_name}} import public, user
@ -24,11 +25,13 @@ def create_app(config_object=ProdConfig):
def register_extensions(app): def register_extensions(app):
db.init_app(app) db.init_app(app)
login_manager.init_app(app)
assets.init_app(app) assets.init_app(app)
return None
def register_blueprints(app): def register_blueprints(app):
# Register blueprints # Register blueprints
app.register_blueprint(public.views.blueprint) app.register_blueprint(public.views.blueprint)
app.register_blueprint(user.views.blueprint) app.register_blueprint(user.views.blueprint)
return app return None

@ -3,3 +3,6 @@
from flask.ext.bcrypt import Bcrypt from flask.ext.bcrypt import Bcrypt
bcrypt = Bcrypt() bcrypt = Bcrypt()
from flask.ext.login import LoginManager
login_manager = LoginManager()

@ -1,19 +1,32 @@
from flask_wtf import Form from flask_wtf import Form
from wtforms import TextField, PasswordField from wtforms import TextField, PasswordField
from wtforms.validators import DataRequired, Email, EqualTo, Length from wtforms.validators import DataRequired
class RegisterForm(Form):
username = TextField('Username',
validators=[DataRequired(), Length(min=3, max=25)])
email = TextField('Email',
validators=[DataRequired(), Email(), Length(min=6, max=40)])
password = PasswordField('Password',
validators=[DataRequired(), Length(min=6, max=40)])
confirm = PasswordField('Verify password',
[DataRequired(), EqualTo('password', message='Passwords must match')])
from {{cookiecutter.repo_name}}.user.models import User
class LoginForm(Form): class LoginForm(Form):
username = TextField('Username', validators=[DataRequired()]) username = TextField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()])
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.user = None
def validate(self):
initial_validation = super(LoginForm, self).validate()
if not initial_validation:
return False
self.user = User.query.filter_by(username=self.username.data).first()
if not self.user:
self.username.errors.append("Unknown username")
return False
if not self.user.check_password(self.password.data):
self.password.errors.append("Invalid password")
return False
if not self.user.active:
self.username.errors.append("User not activated")
return False
return True

@ -2,35 +2,44 @@
'''Public section, including homepage and signup.''' '''Public section, including homepage and signup.'''
from flask import (Blueprint, request, render_template, flash, url_for, from flask import (Blueprint, request, render_template, flash, url_for,
redirect, session) redirect, session)
from flask.ext.login import login_user, login_required, logout_user
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from {{cookiecutter.repo_name}}.extensions import login_manager
from {{cookiecutter.repo_name}}.user.models import User from {{cookiecutter.repo_name}}.user.models import User
from {{cookiecutter.repo_name}}.public.forms import RegisterForm, LoginForm from {{cookiecutter.repo_name}}.public.forms import LoginForm
from {{cookiecutter.repo_name}}.user.forms import RegisterForm
from {{cookiecutter.repo_name}}.utils import flash_errors from {{cookiecutter.repo_name}}.utils import flash_errors
from {{cookiecutter.repo_name}}.database import db from {{cookiecutter.repo_name}}.database import db
blueprint = Blueprint('public', __name__, static_folder="../static") blueprint = Blueprint('public', __name__, static_folder="../static")
@login_manager.user_loader
def load_user(id):
try:
return User.query.get(int(id))
except Exception:
return None
@blueprint.route("/", methods=["GET", "POST"]) @blueprint.route("/", methods=["GET", "POST"])
def home(): def home():
form = LoginForm(request.form) form = LoginForm(request.form)
# Handle logging in
if request.method == 'POST': if request.method == 'POST':
u = User.query.filter_by(username=request.form['username']).first() if form.validate_on_submit():
if u is None or not u.check_password(request.form['password']): login_user(form.user)
error = 'Invalid username or password.'
flash(error, 'warning')
else:
session['logged_in'] = True
session['username'] = u.username
flash("You are logged in.", 'success') flash("You are logged in.", 'success')
return redirect(url_for("user.members")) redirect_url = request.args.get("next") or url_for("user.members")
return redirect(redirect_url)
else:
flash_errors(form)
return render_template("public/home.html", form=form) return render_template("public/home.html", form=form)
@blueprint.route('/logout/') @blueprint.route('/logout/')
@login_required
def logout(): def logout():
session.pop('logged_in', None) logout_user()
session.pop('username', None)
flash('You are logged out.', 'info') flash('You are logged out.', 'info')
return redirect(url_for('public.home')) return redirect(url_for('public.home'))
@ -38,7 +47,10 @@ def logout():
def register(): def register():
form = RegisterForm(request.form, csrf_enabled=False) form = RegisterForm(request.form, csrf_enabled=False)
if form.validate_on_submit(): if form.validate_on_submit():
new_user = User(form.username.data, form.email.data, form.password.data) new_user = User(username=form.username.data,
email=form.email.data,
password=form.password.data,
active=True)
try: try:
db.session.add(new_user) db.session.add(new_user)
db.session.commit() db.session.commit()

@ -20,10 +20,10 @@
<li class="active"><a href="{{ url_for('public.home') }}">Home</a></li> <li class="active"><a href="{{ url_for('public.home') }}">Home</a></li>
<li><a href="{{ url_for('public.about') }}">About</a></li> <li><a href="{{ url_for('public.about') }}">About</a></li>
</ul> </ul>
{% if session.logged_in %} {% if current_user.is_authenticated() %}
<a class="btn btn-default btn-sm navbar-btn navbar-right" href="{{ url_for('public.logout') }}">Log out</a> <a class="btn btn-default btn-sm navbar-btn navbar-right" href="{{ url_for('public.logout') }}">Log out</a>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li><a href="{{ url_for('user.members') }}">Logged in as {{ session.username }}</a></li> <li><a href="{{ url_for('user.members') }}">Logged in as {{ current_user.username }}</a></li>
</ul> </ul>
{% elif form %} {% elif form %}
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">

@ -0,0 +1,36 @@
from flask_wtf import Form
from wtforms import TextField, PasswordField
from wtforms.validators import DataRequired, Email, EqualTo, Length
from .models import User
class RegisterForm(Form):
username = TextField('Username',
validators=[DataRequired(), Length(min=3, max=25)])
email = TextField('Email',
validators=[DataRequired(), Email(), Length(min=6, max=40)])
password = PasswordField('Password',
validators=[DataRequired(), Length(min=6, max=40)])
confirm = PasswordField('Verify password',
[DataRequired(), EqualTo('password', message='Passwords must match')])
def __init__(self, *args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs)
self.user = None
def validate(self):
initial_validation = super(RegisterForm, self).validate()
if not initial_validation:
return False
user = User.query.filter_by(username=self.username.data).first()
if user:
self.username.errors.append("Username already registered")
return False
user = User.query.filter_by(email=self.email.data).first()
if user:
self.email.errors.append("Email already registered")
return False
self.user = user
return True

@ -1,11 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import datetime as dt import datetime as dt
from flask.ext.login import UserMixin
from {{cookiecutter.repo_name}}.database import db from {{cookiecutter.repo_name}}.database import db
from {{cookiecutter.repo_name}}.extensions import bcrypt from {{cookiecutter.repo_name}}.extensions import bcrypt
class User(db.Model): class User(UserMixin, db.Model):
__tablename__ = 'users' __tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -13,11 +15,15 @@ class User(db.Model):
email = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String, nullable=False) password_hash = db.Column(db.String, nullable=False)
created_at = db.Column(db.DateTime(), nullable=False) created_at = db.Column(db.DateTime(), nullable=False)
active = db.Column(db.Boolean())
is_admin = db.Column(db.Boolean())
def __init__(self, username, email, password): def __init__(self, username, email, password, active=False, is_admin=False):
self.username = username self.username = username
self.email = email self.email = email
self.set_password(password) self.set_password(password)
self.active = active
self.is_admin = is_admin
self.created_at = dt.datetime.utcnow() self.created_at = dt.datetime.utcnow()
def set_password(self, password): def set_password(self, password):

@ -1,7 +1,5 @@
from flask import Blueprint, render_template from flask import Blueprint, render_template
from flask.ext.login import login_required
from {{cookiecutter.repo_name}}.utils import login_required
blueprint = Blueprint("user", __name__, url_prefix='/users', blueprint = Blueprint("user", __name__, url_prefix='/users',
static_folder="../static") static_folder="../static")

@ -1,22 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
'''Helper utilities and decorators.''' '''Helper utilities and decorators.'''
from flask import session, flash, redirect, url_for from flask import flash
from functools import wraps
def flash_errors(form): def flash_errors(form):
'''Flash all errors for a form.''' '''Flash all errors for a form.'''
for field, errors in form.errors.items(): for field, errors in form.errors.items():
for error in errors: for error in errors:
flash("Error in the {0} field - {1}" flash("Error in {0} field - {1}"
.format(getattr(form, field).label.text, error), 'warning') .format(getattr(form, field).label.text, error), 'warning')
def login_required(test):
'''Decorator that makes a view require authentication.'''
@wraps(test)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return test(*args, **kwargs)
else:
flash('You need to log in first.')
return redirect(url_for('home'))
return wrap

Loading…
Cancel
Save