From 1f9e6b171e7a35701adec78f3e923de5c7d1905d Mon Sep 17 00:00:00 2001 From: Steven Loria Date: Sun, 1 Dec 2013 13:38:42 -0600 Subject: [PATCH] Store password hash and add basic test Adds Flask-Testing and passlib dependencies --- .../requirements/dev.txt | 1 + .../requirements/prod.txt | 9 +++-- .../{{cookiecutter.repo_name}}/app.py | 1 - .../{{cookiecutter.repo_name}}/models.py | 17 ++++++---- .../modules/public.py | 5 ++- .../{{cookiecutter.repo_name}}/settings.py | 2 +- .../tests/test_models.py | 34 +++++++++++++++++++ .../tests/unit_tests.py | 29 ---------------- 8 files changed, 55 insertions(+), 43 deletions(-) create mode 100644 {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/test_models.py delete mode 100644 {{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/unit_tests.py diff --git a/{{cookiecutter.repo_name}}/requirements/dev.txt b/{{cookiecutter.repo_name}}/requirements/dev.txt index ecbb1f4..aa257a0 100644 --- a/{{cookiecutter.repo_name}}/requirements/dev.txt +++ b/{{cookiecutter.repo_name}}/requirements/dev.txt @@ -3,6 +3,7 @@ # Testing nose +Flask-Testing # Management script Flask-Script diff --git a/{{cookiecutter.repo_name}}/requirements/prod.txt b/{{cookiecutter.repo_name}}/requirements/prod.txt index b8f4800..32cc93e 100644 --- a/{{cookiecutter.repo_name}}/requirements/prod.txt +++ b/{{cookiecutter.repo_name}}/requirements/prod.txt @@ -9,17 +9,20 @@ itsdangerous==0.23 # Database Flask-SQLAlchemy==1.0 -SQLAlchemy==0.8.2 +SQLAlchemy==0.8.3 # Forms Flask-WTF==0.9.2 WTForms==1.0.4 # Deployment -gunicorn==17.5 -wsgiref==0.1.2 +gunicorn>=17.5 +wsgiref>=0.1.2 # Assets Flask-Assets==0.8 cssmin>=0.1.4 jsmin>=2.0.4 + +# Auth +passlib>=1.6.1 diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py index 6adf803..4c5f63c 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/app.py @@ -31,5 +31,4 @@ def create_app(config_object, env): from {{cookiecutter.repo_name}}.modules import public, member app.register_blueprint(public.blueprint) app.register_blueprint(member.blueprint) - return app diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/models.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/models.py index fe93bd5..85eadf4 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/models.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/models.py @@ -4,23 +4,28 @@ {{cookiecutter.project_name}} models. """ from flask.ext.sqlalchemy import SQLAlchemy +from passlib.apps import custom_app_context as pwd_context db = SQLAlchemy() class User(db.Model): __tablename__ = 'users' - id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String, unique=True, nullable=False) - email = db.Column(db.String, unique=True, nullable=False) - password = db.Column(db.String, nullable=False) - + username = db.Column(db.String(80), unique=True, nullable=False) + email = db.Column(db.String(80), unique=True, nullable=False) + password = db.Column(db.String, nullable=False) # The hashed password def __init__(self, username=None, email=None, password=None): self.username = username self.email = email - self.password = password + self.set_password(password) + + def set_password(self, password): + self.password = pwd_context.encrypt(password) + + def check_password(self, password): + return pwd_context.verify(password, self.password) def __repr__(self): return ''.format(username=self.username) diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/modules/public.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/modules/public.py index d69025d..b105812 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/modules/public.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/modules/public.py @@ -18,9 +18,8 @@ blueprint = Blueprint('public', __name__, def home(): form = LoginForm(request.form) if request.method == 'POST': - u = User.query.filter_by(username=request.form['username'], - password=request.form['password']).first() - if u is None: + u = User.query.filter_by(username=request.form['username']).first() + if u is None or not u.check_password(request.form['password']): error = 'Invalid username or password.' flash(error, 'warning') else: diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py index 243bda4..8195659 100644 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/settings.py @@ -13,7 +13,7 @@ class ProdConfig(Config): class DevConfig(Config): DEBUG = True - DB_NAME = "test.db" + DB_NAME = "dev.db" # Put the db file in project root DB_PATH = os.path.join(Config.PROJECT_ROOT, DB_NAME) SQLALCHEMY_DATABASE_URI = "sqlite:///{0}".format(DB_PATH) diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/test_models.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/test_models.py new file mode 100644 index 0000000..34c8cf3 --- /dev/null +++ b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/test_models.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +import unittest +from nose.tools import * # PEP8 asserts +from flask.ext.testing import TestCase + +from {{ cookiecutter.repo_name }}.app import create_app +from {{ cookiecutter.repo_name }}.models import User, db + + +class TestUser(TestCase): + TESTING = True + DEBUG = True + SQLALCHEMY_DATABASE_URI = 'sqlite://' + + def create_app(self): + app = create_app(self, 'testing') + with app.app_context(): + db.create_all() + return app + + def tearDown(self): + db.session.remove() + db.drop_all() + + def test_check_password(self): + user = User(username="foo", email="foo@bar.com", + password="foobarbaz123") + db.session.add(user) + db.session.commit() + assert_true(user.check_password('foobarbaz123')) + assert_false(user.check_password("barfoobaz")) + +if __name__ == '__main__': + unittest.main() diff --git a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/unit_tests.py b/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/unit_tests.py deleted file mode 100644 index 51fbb28..0000000 --- a/{{cookiecutter.repo_name}}/{{cookiecutter.repo_name}}/tests/unit_tests.py +++ /dev/null @@ -1,29 +0,0 @@ -'''Unit testing''' - -import unittest -try: - from nose.tools import * # PEP8 asserts -except ImportError: - import sys - print('nose required. Run "pip install nose".') - -from {{cookiecutter.repo_name}}.main import create_app - -class Test{{cookiecutter.repo_name | capitalize}}(unittest.TestCase): - - def setUp(self): - app = create_app("{{cookiecutter.repo_name}}.settings.DevConfig", 'dev') - app.config['TESTING'] = True - self.app = app.test_client() - - def test_add(self): - '''An example test.''' - assert_equal(1 + 1, 2) - -def json_response(response, code=200): - '''Checks that the status code is OK and returns the json as a dict.''' - assert_equal(response.status_code, code) - return json.loads(response.data) - -if __name__ == '__main__': - unittest.main()