# Path: bitsd/persistence/query.py
# def get_user(session, username):
#     """Get user by name."""
#     if not username:
#         return None
#     return query_by_attribute(session, User, 'name', username)
# 
# def get_last_login_attempt(session, ip_address, username=None):
#     """Get last failed login attempt for given IP address. Filter by username if provided"""
#     L = LoginAttempt  # Shortcut
#     if username is not None:
#         return session.query(L).filter(L.ipaddress == ip_address, L.username == username).order_by(desc(L.timestamp)).first()
#     else:
#         return session.query(L).filter(L.ipaddress == ip_address).order_by(desc(L.timestamp)).first()
# 
# def log_last_login_attempt(session, ip_address, username):
#     """"Persist failed login attempt (either insert or update)"""
#     attempt = LoginAttempt(username, ip_address)
#     attempt = session.merge(attempt)
#     return persist(session, attempt)
# 
# Path: bitsd/persistence/models.py
# class User(Base):
#     """User name/password hash entity."""
#     __tablename__ = 'User'
#     __table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8mb4'}
# 
#     userid = Column(Integer, primary_key=True)
#     name = Column(String(length=256), unique=True, nullable=False)
#     password = Column(String(length=512), nullable=False)
# 
#     def __init__(self, name, pwhash):
#         self.name = name
#         self.password = pwhash
# 
#     def __str__(self):
#         return '{self.name}: {self.password}'.format(self=self)
# 
# Path: bitsd/persistence/engine.py
# def persist(session, data, flush=True):
#     """Persist data to configured DB and return persisted object
#     in a consistent state.
# 
#     **Note:** might log what's being persisted, so don't put clear text password
#     into `__str__()`."""
#     LOG.debug('Persisting data %r', data)
#     session.add(data)
#     if flush:
#         session.flush()
#     return data
# 
# def delete(session, data):
#     """Delete data from DB."""
#     LOG.debug('Deleting %r', data)
#     session.delete(data)
# 
# Path: bitsd/common.py
# LOG = logging.getLogger('tornado.general')


from datetime import datetime, timedelta
from passlib.hash import sha512_crypt as Hasher
from tornado.options import options
from recaptcha import RecaptchaClient
from bitsd.persistence.query import get_user, get_last_login_attempt, log_last_login_attempt
from bitsd.persistence.models import User
from bitsd.persistence.engine import persist, delete
from bitsd.common import LOG

    user = get_user(session, username)

    if user is None:
        LOG.warn("Failed attempt for non existent user %r", username)
        # Calculate hash anyway (see docs for the explanation)
        Hasher.encrypt(supplied_password)
        log_last_login_attempt(session, ip_address, username)
        return False
    else:
        valid = Hasher.verify(supplied_password, user.password)
        if not valid:
            log_last_login_attempt(session, ip_address, username)
        return valid


def useradd(session, username, password):
    """Add user with hashed password to database"""
    user = User(username, Hasher.encrypt(password))
    persist(session, user)


def userdel(session, username):
    """Delete user from database."""
    user = get_user(session, username)
    delete(session, user)


def usermod(session, username, password):
    """"Modify password for existing user."""

# Path: bitsd/persistence/query.py
# def get_user(session, username):
#     """Get user by name."""
#     if not username:
#         return None
#     return query_by_attribute(session, User, 'name', username)
# 
# def get_last_login_attempt(session, ip_address, username=None):
#     """Get last failed login attempt for given IP address. Filter by username if provided"""
#     L = LoginAttempt  # Shortcut
#     if username is not None:
#         return session.query(L).filter(L.ipaddress == ip_address, L.username == username).order_by(desc(L.timestamp)).first()
#     else:
#         return session.query(L).filter(L.ipaddress == ip_address).order_by(desc(L.timestamp)).first()
# 
# def log_last_login_attempt(session, ip_address, username):
#     """"Persist failed login attempt (either insert or update)"""
#     attempt = LoginAttempt(username, ip_address)
#     attempt = session.merge(attempt)
#     return persist(session, attempt)
# 
# Path: bitsd/persistence/models.py
# class User(Base):
#     """User name/password hash entity."""
#     __tablename__ = 'User'
#     __table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8mb4'}
# 
#     userid = Column(Integer, primary_key=True)
#     name = Column(String(length=256), unique=True, nullable=False)
#     password = Column(String(length=512), nullable=False)
# 
#     def __init__(self, name, pwhash):
#         self.name = name
#         self.password = pwhash
# 
#     def __str__(self):
#         return '{self.name}: {self.password}'.format(self=self)
# 
# Path: bitsd/persistence/engine.py
# def persist(session, data, flush=True):
#     """Persist data to configured DB and return persisted object
#     in a consistent state.
# 
#     **Note:** might log what's being persisted, so don't put clear text password
#     into `__str__()`."""
#     LOG.debug('Persisting data %r', data)
#     session.add(data)
#     if flush:
#         session.flush()
#     return data
# 
# def delete(session, data):
#     """Delete data from DB."""
#     LOG.debug('Deleting %r', data)
#     session.delete(data)
# 
# Path: bitsd/common.py
# LOG = logging.getLogger('tornado.general')


# Path: bitsd/server/auth.py
from datetime import datetime, timedelta
from passlib.hash import sha512_crypt as Hasher
from tornado.options import options
from recaptcha import RecaptchaClient
from bitsd.persistence.query import get_user, get_last_login_attempt, log_last_login_attempt
from bitsd.persistence.models import User
from bitsd.persistence.engine import persist, delete
from bitsd.common import LOG


    user = get_user(session, username)

    if user is None:
        LOG.warn("Failed attempt for non existent user %r", username)
        # Calculate hash anyway (see docs for the explanation)
        Hasher.encrypt(supplied_password)
        log_last_login_attempt(session, ip_address, username)
        return False
    else:
        valid = Hasher.verify(supplied_password, user.password)
        if not valid:
            log_last_login_attempt(session, ip_address, username)
        return valid


def useradd(session, username, password):
    """Add user with hashed password to database"""
    user = User(username, Hasher.encrypt(password))
    persist(session, user)


def userdel(session, username):
    """Delete user from database."""
    user = get_user(session, username)
    delete(session, user)


def usermod(session, username, password):
    """"Modify password for existing user."""

    user = get_user(session, username)