Skip to content

HashProperty

Mike Bayer edited this page Feb 13, 2014 · 1 revision

Note: Please see UsageRecipes/DatabaseCrypt for a modernized version of database-side crypt() mapping.

Hashed property

Attached is a small library (with no dependency on SQLAlchemy) to implement hashed storage of passwords. Example usage in SQLAlchemy context below:

#!python
from sqlalchemy import create_engine, Column, Integer, String, func
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

from hashed_property import HashedProperty

# Set up a standard declarative base class and an engine
Base = declarative_base(bind=create_engine('mysql://root@localhost/test'))

# A user class
class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True)
    username = Column(String(100), nullable=False)
    
    # These are the columns that will hold the values
    password_hash = Column(String(40), nullable=False)
    password_salt = Column(String(40), nullable=False)
    
    # This is the proprty that will be used as interface
    password = HashedProperty('password_hash', 'password_salt',
        # Optional function to enable generating the hash on the db side
        dbhashfunc=(lambda pw,salt: func.sha1(pw + salt)))
    
    # For pretty printing
    def __repr__(self):
        return "User(id=%r, username=%r)" % (self.id, self.username)

# Create tables if not yet created
Base.metadata.create_all(checkfirst=True)

# Turn on echoing so we can see SQL
Base.metadata.bind.echo = True

# A standard session
session = sessionmaker()()

# Clear data
session.query(User).delete()


# Create an user and store it
u1 = User(username='foo', password='foobar')
session.add(u1)
session.commit()

# Password can be checked client side
print u1.password == 'foobar', u1.password == 'asdf'
# And server side
print session.query(User).filter(User.password == 'foobar').all()
print session.query(User).filter(User.password == 'asdf').all()

# Password can be updated
u1.password = 'asdf'
session.flush()
print session.query(User).filter(User.password == 'asdf').all()
Clone this wiki locally