From 8b95f4c0545de0e6a73ceaa9dab837a8639e7231 Mon Sep 17 00:00:00 2001 From: kelepirci Date: Sun, 10 Jul 2016 19:08:31 +0300 Subject: [PATCH] Basic auth system implimented Basic user authantication has implimented with Flask-Login and pass word hasing with werkzeg. --- dash/__init__.py | 22 +++++++++++++++++++- dash/auth/__init__.py | 5 +++++ dash/auth/forms.py | 12 +++++++++++ dash/auth/views.py | 24 +++++++++++++++++++++ dash/main/views.py | 31 +++++++--------------------- dash/models.py | 31 +++++++++++++++++++++++----- dash/templates/{ => auth}/login.html | 14 ++++++++----- dash/templates/index.html | 6 ++++-- migrations/versions/24ea14f8cb4e_.py | 30 +++++++++++++++++++++++++++ migrations/versions/28f38d9e7724_.py | 26 +++++++++++++++++++++++ tests/test_user_model.py | 22 ++++++++++++++++++++ 11 files changed, 186 insertions(+), 37 deletions(-) create mode 100644 dash/auth/__init__.py create mode 100644 dash/auth/forms.py create mode 100644 dash/auth/views.py rename dash/templates/{ => auth}/login.html (69%) create mode 100644 migrations/versions/24ea14f8cb4e_.py create mode 100644 migrations/versions/28f38d9e7724_.py create mode 100644 tests/test_user_model.py diff --git a/dash/__init__.py b/dash/__init__.py index 27898df..5246778 100644 --- a/dash/__init__.py +++ b/dash/__init__.py @@ -1,8 +1,11 @@ from flask import Flask, render_template + from flask_adminlte import AdminLTE from flask_mail import Mail from flask_moment import Moment from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager + from config import config AdminLTE = AdminLTE() @@ -10,20 +13,37 @@ mail = Mail() moment = Moment() db = SQLAlchemy() +# initialize flask_login +login_manager = LoginManager() +login_manager.session_protection = 'strong' +login_manager.login_view = 'auth.login' + +# user loader call back function +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) + def create_app(config_name): dash = Flask(__name__) dash.config.from_object(config[config_name]) config[config_name].init_app(dash) - + + AdminLTE.init_app(dash) mail.init_app(dash) moment.init_app(dash) db.init_app(dash) + login_manager.init_app(dash) # attach routes and custom error pages here + # main application from main import main as main_blueprint dash.register_blueprint(main_blueprint) + # auth application + from .auth import auth as auth_blueprint + dash.register_blueprint(auth_blueprint, url_prefix='/auth') + return dash \ No newline at end of file diff --git a/dash/auth/__init__.py b/dash/auth/__init__.py new file mode 100644 index 0000000..777f91f --- /dev/null +++ b/dash/auth/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +auth = Blueprint('auth', __name__) + +from . import views \ No newline at end of file diff --git a/dash/auth/forms.py b/dash/auth/forms.py new file mode 100644 index 0000000..6cbec04 --- /dev/null +++ b/dash/auth/forms.py @@ -0,0 +1,12 @@ +from flask_wtf import Form + +from wtforms import StringField, PasswordField, BooleanField, SubmitField +from wtforms.validators import Required, Length, Email + +class LoginForm(Form): + email = StringField('Email', validators=[Required(), Length(1, 128), + Email()]) + password = PasswordField('Password', validators=[Required()]) + remember_me = BooleanField('Keep me logged in') + submit = SubmitField('Log In') + \ No newline at end of file diff --git a/dash/auth/views.py b/dash/auth/views.py new file mode 100644 index 0000000..0b4666f --- /dev/null +++ b/dash/auth/views.py @@ -0,0 +1,24 @@ +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', methods=['GET', 'POST']) +def login(): + 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) + return redirect(request.args.get('next') or url_for('main.index')) + 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')) \ No newline at end of file diff --git a/dash/main/views.py b/dash/main/views.py index 18a12b8..a5da6ed 100644 --- a/dash/main/views.py +++ b/dash/main/views.py @@ -1,4 +1,9 @@ +import dateutil.parser + from flask import render_template, session, redirect, url_for, current_app + +from flask_login import login_required + from .. import db from ..models import User from ..email import send_email @@ -7,31 +12,9 @@ from .forms import NameForm @main.route('/', methods=['GET', 'POST']) +@login_required def index(): - form = NameForm() - current_user = User() - 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) - session['known'] = False - if current_app.config['DASH_STACK_ADMIN']: - send_email(current_app.config['DASH_STACK_ADMIN'], 'New User', - 'mail/new_user', user=user) - else: - session['known'] = True - session['name'] = form.name.data - return redirect(url_for('.index')) - return render_template('index.html', - form=form, name=session.get('name'), - known=session.get('known', False), - current_user=current_user) - -@main.route('/login') -def login(): - current_user = User() - return render_template('login.html', current_user=current_user) + return render_template('index.html') @main.route('/lockscreen') def lockscreen(): diff --git a/dash/models.py b/dash/models.py index febd562..36512a3 100644 --- a/dash/models.py +++ b/dash/models.py @@ -1,7 +1,15 @@ import dateutil.parser +import datetime +from werkzeug.security import generate_password_hash, check_password_hash + +from flask_login import UserMixin from . import db +from . import login_manager +@login_manager.user_loader +def load_user(user_id): + return User.query.get(int(user_id)) class Role(db.Model): __tablename__ = 'roles' @@ -14,15 +22,28 @@ 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(128), unique=True, index=True) username = db.Column(db.String(64), unique=True, index=True) - full_name = db.Column(db.String(256), index=True) - avatar = db.Column(db.String(256), index=True) - created_at = dateutil.parser.parse("November 12, 2012") + password_hash = db.Column(db.String(128)) + full_name = db.Column(db.String(255), index=True) + avatar = db.Column(db.String(255), index=True) + created_at = db.Column(db.DateTime) role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) + + @property + def password(self): + raise AttributeError('password is not a readable attribute') + + @password.setter + def password(self, password): + self.password_hash = generate_password_hash(password) + + def verify_password(self, password): + return check_password_hash(self.password_hash, password) def __repr__(self): - return '' % self.username \ No newline at end of file + return '' % self.username diff --git a/dash/templates/login.html b/dash/templates/auth/login.html similarity index 69% rename from dash/templates/login.html rename to dash/templates/auth/login.html index c80d01f..27ca879 100644 --- a/dash/templates/login.html +++ b/dash/templates/auth/login.html @@ -8,7 +8,7 @@