Fixed issues with user authentication.
Improved setup and minor improvements.
This commit is contained in:
parent
6bf54f9fcd
commit
9bb664b097
|
@ -1,11 +1,10 @@
|
||||||
Things To-Do
|
Things To-Do
|
||||||
============
|
============
|
||||||
|
|
||||||
* Build out setup.py and make it easy to distribute on PyPI.
|
|
||||||
* Build out user account management and registration. {Started}
|
* Build out user account management and registration. {Started}
|
||||||
* Make new logo based on APS' Mr. Penguin. {Started}
|
* Make new logo based on APS' Mr. Penguin. {Started}
|
||||||
* Build out simple to-do/project creation and management setup.
|
* Build out simple to-do/project creation and management setup.
|
||||||
* Setup a nice clutter-free deployment option for rookeri.es itself. (In-progress)
|
* Setup a nice clutter-free deployment option for rookeri.es itself. {In-progress}
|
||||||
|
|
||||||
* Build out documentation with Sphinx.
|
* Build out documentation with Sphinx.
|
||||||
** Make Sphinx output and site look-and-feel match.
|
** Make Sphinx output and site look-and-feel match.
|
||||||
|
@ -20,3 +19,4 @@ Things Done
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
* *2013 May 09* --- Transitioned COPYING to reStructured text.
|
* *2013 May 09* --- Transitioned COPYING to reStructured text.
|
||||||
|
* *2013 June 11* --- Build out setup.py and make it easy to distribute on PyPI.
|
||||||
|
|
|
@ -23,16 +23,14 @@ Admin module for Rookeries that adds some special utilities to run outside of th
|
||||||
@author: Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
@author: Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from rookeries.core.database import db_session, init_db
|
from rookeries.core.database import init_db
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def rebuild_database():
|
def rebuild_database():
|
||||||
"""Re-synchronizes the database which is pretty useful for rapid development."""
|
"""Re-synchronizes the database which is pretty useful for rapid development."""
|
||||||
from rookeries.core.models import populate_db_with_user_setup
|
# TODO Add in some nice switches later...
|
||||||
|
init_db(True, True)
|
||||||
init_db()
|
|
||||||
populate_db_with_user_setup(db_session)
|
|
||||||
|
|
||||||
|
|
||||||
def main(command):
|
def main(command):
|
||||||
|
|
|
@ -28,6 +28,7 @@ Core module that handles database connections in Rookeri.es
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
|
||||||
from rookeries.core.config import Config
|
from rookeries.core.config import Config
|
||||||
|
|
||||||
engine = create_engine(Config().DATABASE_URI, convert_unicode=True)
|
engine = create_engine(Config().DATABASE_URI, convert_unicode=True)
|
||||||
|
@ -37,18 +38,16 @@ Base = declarative_base()
|
||||||
Base.query = db_session.query_property()
|
Base.query = db_session.query_property()
|
||||||
|
|
||||||
|
|
||||||
def init_db():
|
def init_db(drop_tables=False, populate_with_data=False):
|
||||||
"""Initializes the database and bindings to the models."""
|
"""Initializes the database and bindings to the models. Also populates the models with initial data."""
|
||||||
# import all modules here that might define models so that
|
|
||||||
# they will be registered properly on the metadata. Otherwise
|
|
||||||
# you will have to import them first before calling init_db()
|
|
||||||
import rookeries.core.models
|
import rookeries.core.models
|
||||||
|
|
||||||
|
if drop_tables:
|
||||||
|
Base.metadata.drop_all(bind=engine)
|
||||||
|
|
||||||
Base.metadata.create_all(bind=engine)
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
if drop_tables and populate_with_data:
|
||||||
def resync_db():
|
# TODO Add in some nice checks for database migration??
|
||||||
"""Re-synchronizes the database which is pretty useful for rapid development."""
|
rookeries.core.models.populate_db_with_init_models(db_session)
|
||||||
from rookeries.core.models import populate_db_with_user_setup
|
|
||||||
|
|
||||||
init_db()
|
|
||||||
populate_db_with_user_setup(db_session)
|
|
|
@ -44,6 +44,9 @@ class User(Base):
|
||||||
full_name = Column(String(256))
|
full_name = Column(String(256))
|
||||||
email = Column(String(128), unique=True)
|
email = Column(String(128), unique=True)
|
||||||
|
|
||||||
|
user_auth_id = Column(Integer, ForeignKey("user_authentication.id"))
|
||||||
|
user_authentication = relationship("UserAuthentication", backref="user", cascade="all, delete")
|
||||||
|
|
||||||
user_type_id = Column(Integer, ForeignKey('user_type.id'))
|
user_type_id = Column(Integer, ForeignKey('user_type.id'))
|
||||||
user_type = relationship("UserType", backref=backref('users', order_by=id))
|
user_type = relationship("UserType", backref=backref('users', order_by=id))
|
||||||
|
|
||||||
|
@ -56,16 +59,12 @@ class User(Base):
|
||||||
return "<User('%s', '%s', '%s')>" % (self.username, self.email, self.full_name)
|
return "<User('%s', '%s', '%s')>" % (self.username, self.email, self.full_name)
|
||||||
|
|
||||||
|
|
||||||
class UserAuthenticaton(Base):
|
class UserAuthentication(Base):
|
||||||
"""
|
"""Model for storing and managing user authentication."""
|
||||||
Separate setup for storing and managing user authentication.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__tablename__ = "user_authentication"
|
__tablename__ = "user_authentication"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
user_id = Column(Integer, ForeignKey("user.id"))
|
|
||||||
user = relationship("User", backref=backref("user_authentication", order_by=id))
|
|
||||||
|
|
||||||
password = Column(String(512))
|
password = Column(String(512))
|
||||||
security_update_date = Column(DateTime)
|
security_update_date = Column(DateTime)
|
||||||
|
@ -73,9 +72,8 @@ class UserAuthenticaton(Base):
|
||||||
status_id = Column(Integer, ForeignKey("status.id"))
|
status_id = Column(Integer, ForeignKey("status.id"))
|
||||||
status = relationship("Status")
|
status = relationship("Status")
|
||||||
|
|
||||||
def __init__(self, user):
|
def __init__(self):
|
||||||
self.user = user
|
self.status = Status.query.filter(Status.name == "USER_UNKNOWN").first()
|
||||||
self.status = db_session.query(Status, name="USER_UNKNOWN")
|
|
||||||
self.security_update_date = datetime.now()
|
self.security_update_date = datetime.now()
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,9 +112,7 @@ class Status(Base):
|
||||||
|
|
||||||
|
|
||||||
# TODO Organize later...
|
# TODO Organize later...
|
||||||
def populate_db_with_user_setup(db_session):
|
def populate_db_with_init_models(db_session):
|
||||||
|
|
||||||
# TODO Make something nicer that queries before adding in a new entry...
|
|
||||||
|
|
||||||
# Add in types for users.
|
# Add in types for users.
|
||||||
admin_user_type = UserType("admin", True, True)
|
admin_user_type = UserType("admin", True, True)
|
||||||
|
|
|
@ -31,50 +31,50 @@ from datetime import datetime
|
||||||
from rookeries.core.config import Config
|
from rookeries.core.config import Config
|
||||||
|
|
||||||
|
|
||||||
def generate_password_hash(username, user_auth, password, site_secret):
|
def generate_password_hash(user, password, site_secret):
|
||||||
"""
|
"""
|
||||||
Generates a proper secure salt with hash attached to a user.
|
Generates a proper secure salt with hash attached to a user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Inspired by this article on proper handling of salts: http://crackstation.net/hashing-security.htm
|
# Inspired by this article on proper handling of salts: http://crackstation.net/hashing-security.htm
|
||||||
user_sec_update_date = datetime.isoformat(user_auth.security_update_date)
|
user_sec_update_date = datetime.isoformat(user.user_authentication.security_update_date)
|
||||||
|
|
||||||
hashable = username + ";" + password + ";" + site_secret + ";" + user_sec_update_date
|
hashable = user.username + ";" + password + ";" + site_secret + ";" + user_sec_update_date
|
||||||
hasher = hashlib.sha512()
|
hasher = hashlib.sha512()
|
||||||
hasher.update(hashable)
|
hasher.update(hashable)
|
||||||
|
|
||||||
return unicode(hasher.hexdigest())
|
return unicode(hasher.hexdigest())
|
||||||
|
|
||||||
|
|
||||||
def check_password(username, user_auth, password):
|
def check_password(user, password):
|
||||||
valid_password = False
|
valid_password = False
|
||||||
|
|
||||||
config = Config()
|
config = Config()
|
||||||
if user_auth is not None:
|
if user is not None:
|
||||||
attempted_credentials = generate_password_hash(username, user_auth, password, config.SITE_SECRET)
|
attempted_credentials = generate_password_hash(user, password, config.SITE_SECRET)
|
||||||
actual_credentials = user_auth.password
|
actual_credentials = user.user_authentication.password
|
||||||
valid_password = (actual_credentials == attempted_credentials)
|
valid_password = (actual_credentials == attempted_credentials)
|
||||||
|
|
||||||
return valid_password
|
return valid_password
|
||||||
|
|
||||||
|
|
||||||
def change_password(username, user_auth, password):
|
def change_password(user, password):
|
||||||
"""
|
"""
|
||||||
Generate the password hash and change it in the object.
|
Generate the password hash and change it in the object.
|
||||||
"""
|
"""
|
||||||
user_auth.security_update_date = datetime.now()
|
user.user_authentication.security_update_date = datetime.now()
|
||||||
hashed_password = generate_password_hash(username, user_auth, password, Config.SITE_SECRET)
|
hashed_password = generate_password_hash(user, password, Config.SITE_SECRET)
|
||||||
user_auth.password = hashed_password
|
user.user_authentication.password = hashed_password
|
||||||
|
|
||||||
|
|
||||||
def check_user_login(user, user_auth, password):
|
def check_user_login(user, password):
|
||||||
"""
|
"""
|
||||||
Checks to see if the user can login.
|
Checks to see if the user can login.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if check_password(user, user_auth, password):
|
if check_password(user, password):
|
||||||
# TODO Use a better thing than just magic strings to check status.
|
# TODO Use a better thing than just magic strings to check status.
|
||||||
return user_auth.status.name == "USER_ACTIVE"
|
return user.user_authentication.status.name == "USER_ACTIVE"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
# TODO Add in comments plus all requistes from requirements.txt
|
|
||||||
# TODO Look into http://stackoverflow.com/questions/4150423/can-pip-install-dependencies-not-specified-in-setup-py-at-install-time
|
|
||||||
|
|
||||||
setup(name='rookeries',
|
|
||||||
version='0.2',
|
|
||||||
description='Python Distribution Utilities',
|
|
||||||
author='Dorian Pula',
|
|
||||||
author_email='dorian.pula@amber-penguin-software.ca',
|
|
||||||
url='http://rookeri.es',
|
|
||||||
packages=['distutils', 'distutils.command'],
|
|
||||||
install_requires= open('app/requirements.txt').readlines(),
|
|
||||||
# install_requires=['setuptools'],
|
|
||||||
)
|
|
|
@ -30,7 +30,7 @@ import os
|
||||||
import datetime
|
import datetime
|
||||||
from rookeries.core.config import Config
|
from rookeries.core.config import Config
|
||||||
from rookeries.core.database import db_session
|
from rookeries.core.database import db_session
|
||||||
from rookeries.core.models import User, UserAuthenticaton
|
from rookeries.core.models import User, UserAuthentication
|
||||||
from rookeries.core.security import check_password, generate_password_hash
|
from rookeries.core.security import check_password, generate_password_hash
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
@ -53,10 +53,9 @@ def login():
|
||||||
|
|
||||||
# TODO Do try / catch or deal with exceptions?
|
# TODO Do try / catch or deal with exceptions?
|
||||||
user = User.query.filter(User.username == username).first()
|
user = User.query.filter(User.username == username).first()
|
||||||
user_auth = UserAuthenticaton.query.filter(UserAuthenticaton.user == user).first()
|
|
||||||
|
|
||||||
# TODO Do try / catch or deal with exceptions?
|
# TODO Do try / catch or deal with exceptions?
|
||||||
valid_password = check_password(user, user_auth, password)
|
valid_password = check_password(user, password)
|
||||||
if valid_password is True:
|
if valid_password is True:
|
||||||
# TODO Add in proper authentication tokens instead...
|
# TODO Add in proper authentication tokens instead...
|
||||||
session["logged_in"] = True
|
session["logged_in"] = True
|
||||||
|
@ -163,11 +162,10 @@ def register_user():
|
||||||
|
|
||||||
# TODO Add in validation of email... and status that requires email validation of user.
|
# TODO Add in validation of email... and status that requires email validation of user.
|
||||||
user = User(username=username, full_name=user_full_name, email=email)
|
user = User(username=username, full_name=user_full_name, email=email)
|
||||||
|
user.user_authentication = UserAuthentication()
|
||||||
|
|
||||||
|
|
||||||
user.security_update_date = datetime.datetime.now()
|
|
||||||
hashed_password = generate_password_hash(user, password, Config.SITE_SECRET)
|
hashed_password = generate_password_hash(user, password, Config.SITE_SECRET)
|
||||||
user.password = hashed_password
|
user.user_authentication.password = hashed_password
|
||||||
# TODO Fix user types... not working very well at the moment... as in not at all.
|
# TODO Fix user types... not working very well at the moment... as in not at all.
|
||||||
# user.user_type = UserType.id
|
# user.user_type = UserType.id
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2013 Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
||||||
|
#
|
||||||
|
# Rookeries is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of
|
||||||
|
# the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Rookeries is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public
|
||||||
|
# License along with Rookeries. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Please share and enjoy!
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Setup for Rookeries web app
|
||||||
|
|
||||||
|
@author: Dorian Pula <dorian.pula@amber-penguin-software.ca>
|
||||||
|
"""
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
# Inspired by
|
||||||
|
# http://stackoverflow.com/questions/4150423/can-pip-install-dependencies-not-specified-in-setup-py-at-install-time
|
||||||
|
|
||||||
|
setup(name='rookeries',
|
||||||
|
version='0.0.2',
|
||||||
|
description='Rookeri.es Task Centric Productivity Suite',
|
||||||
|
author='Dorian Pula',
|
||||||
|
author_email='dorian.pula@amber-penguin-software.ca',
|
||||||
|
url='http://rookeri.es/',
|
||||||
|
packages=['rookeries', 'rookeries.core'],
|
||||||
|
install_requires=open('requirements.txt').readlines(),)
|
Loading…
Reference in New Issue