<commit_before># -*- coding: utf-8 -*-
'''
Created on Mar 12, 2012

@author: moloch

    Copyright 2012 Root the Box

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
----------------------------------------------------------------------------

This file contiains the user object, used to store data related to an
indiviudal user, such as handle/account/password/etc

'''


import scrypt

from os import urandom
from uuid import uuid4
from hashlib import md5, sha1, sha256
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import synonym, relationship, backref
from sqlalchemy.types import Unicode, Integer, String
from models import dbsession, Team, Permission
from models.MarketItem import MarketItem
from models.BaseGameObject import BaseObject
from string import ascii_letters, digits, printable


class User(BaseObject):
    ''' User definition '''

    _account = Column(Unicode(64), unique=True, nullable=False)
    account = synonym('_account', descriptor=property(
        lambda self: self._account,
        lambda self, account: setattr(
            self, '_account', self.__class__.filter_string(account, "_-"))
    ))
    _handle = Column(Unicode(64), unique=True, nullable=False)
    handle = synonym('_handle', descriptor=property(
        lambda self: self._handle,
        lambda self, handle: setattr(
            self, '_handle', self.__class__.filter_string(handle, "_-"))
    ))
    team_id = Column(Integer, ForeignKey('team.id'))
    permissions = relationship("Permission", backref=backref(
        "User", lazy="joined"), cascade="all, delete-orphan")
    notifications = relationship("Notification", backref=backref(
        "User", lazy="select"), cascade="all, delete-orphan")
    avatar = Column(Unicode(64), default=unicode("default_avatar.jpeg"))
    _password = Column('password', Unicode(128))
    password = synonym('_password', descriptor=property(
        lambda self: self._password,
        lambda self, password: setattr(
            self, '_password', self.__class__._hash_password(self.algorithm, password, self.salt))
    ))
    uuid = Column(Unicode(36), unique=True, nullable=False, default=lambda: unicode(uuid4()))
    algorithm = Column(Unicode(8), default=u"md5", nullable=False)
    theme_id = Column(Integer, ForeignKey('theme.id'), default=3, nullable=False)
    psk = Column(Unicode(64), default=lambda: unicode(urandom(32).encode('hex')))
    salt = Column(String(32), default=lambda: urandom(16).encode('hex'))
    algorithms = {'md5': md5, 'sha1': sha1, 'sha256': sha256}

    @classmethod
    def all(cls):
        ''' Returns a list of all objects in the database '''
        return dbsession.query(cls).all()

    @classmethod
    def all_users(cls):
        ''' Return all non-admin user objects '''
        return filter(lambda user: user.has_permission('admin') is False, cls.all())

    @classmethod
    def by_id(cls, identifier):
        ''' Returns a the object with id of identifier '''
        return dbsession.query(cls).filter_by(id=identifier).first()

    @classmethod
    def by_uuid(cls, uuid):
        ''' Return and object based on a uuid '''
        return dbsession.query(cls).filter_by(uuid=unicode(uuid)).first()

    @classmethod
    def by_account(cls, account):
        ''' Return the user object whose user account is "account" '''
        return dbsession.query(cls).filter_by(account=unicode(account)).first()

    @classmethod
    def by_handle(cls, handle):
        ''' Return the user object whose user is "handle" '''
        return dbsession.query(cls).filter_by(handle=unicode(handle)).first()

    @classmethod
    def filter_string(cls, string, extra_chars=''):
        char_white_list = ascii_letters + digits + extra_chars
        return filter(lambda char: char in char_white_list, string)

    @classmethod
    def _hash_password(cls, algorithm_name, password, salt):
        '''
        Hashes the password using Md5/Sha1/Sha256/Scrypt; scrypt
        should only used for the admin accounts.

        @param algorithm_name: The hashing algorithm to be used
        @param password: Preimage to be hashed, non-ascii chars are ignored
        @param salt: Salt for password, only used with the scrypt algorithm
        @return: Unicode hexadecimal string of the hash digest
        @rtype: unicode
        '''
        password = filter(lambda char: char in printable[:-5], password)
        if algorithm_name == 'scrypt':
            return cls.__scrypt__(password, salt)
        elif algorithm_name in cls.algorithms:
            algo = cls.algorithms[algorithm_name]()
            algo.update(password)
            return unicode(algo.hexdigest())
        else:
            raise ValueError("Algorithm not supported.")

    @classmethod
    def __scrypt__(cls, password, salt):
        '''
        Uses scrypt to hash the password using a random salt

        @param password: The preimage to be hashed
        @param salt: The auto-generated hash<commit_msg>Fixed small bug in User model<commit_after># -*- coding: utf-8 -*-
'''
Created on Mar 12, 2012

@author: moloch

    Copyright 2012 Root the Box

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
----------------------------------------------------------------------------

This file contiains the user object, used to store data related to an
indiviudal user, such as handle/account/password/etc

'''


import scrypt

from os import urandom
from uuid import uuid4
from hashlib import md5, sha1, sha256
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import synonym, relationship, backref
from sqlalchemy.types import Unicode, Integer, String
from models import dbsession, Team, Permission
from models.MarketItem import MarketItem
from models.BaseGameObject import BaseObject
from string import ascii_letters, digits, printable


class User(BaseObject):
    ''' User definition '''

    _account = Column(Unicode(64), unique=True, nullable=False)
    account = synonym('_account', descriptor=property(
        lambda self: self._account,
        lambda self, account: setattr(
            self, '_account', self.__class__.filter_string(account, "_-"))
    ))
    _handle = Column(Unicode(64), unique=True, nullable=False)
    handle = synonym('_handle', descriptor=property(
        lambda self: self._handle,
        lambda self, handle: setattr(
            self, '_handle', self.__class__.filter_string(handle, "_-"))
    ))
    team_id = Column(Integer, ForeignKey('team.id'))
    permissions = relationship("Permission", backref=backref(
        "User", lazy="joined"), cascade="all, delete-orphan")
    notifications = relationship("Notification", backref=backref(
        "User", lazy="select"), cascade="all, delete-orphan")
    avatar = Column(Unicode(64), default=unicode("default_avatar.jpeg"))
    _password = Column('password', Unicode(128))
    password = synonym('_password', descriptor=property(
        lambda self: self._password,
        lambda self, password: setattr(
            self, '_password', self.__class__._hash_password(self.algorithm, password, self.salt))
    ))
    uuid = Column(Unicode(36), unique=True, nullable=False, default=lambda: unicode(uuid4()))
    algorithm = Column(Unicode(8), default=u"md5", nullable=False)
    theme_id = Column(Integer, ForeignKey('theme.id'), default=3, nullable=False)
    psk = Column(Unicode(64), default=lambda: unicode(urandom(32).encode('hex')))
    salt = Column(String(32), default=lambda: urandom(16).encode('hex'))
    algorithms = {'md5': md5, 'sha1': sha1, 'sha256': sha256}

    @classmethod
    def all(cls):
        ''' Returns a list of all objects in the database '''
        return dbsession.query(cls).all()

    @classmethod
    def all_users(cls):
        ''' Return all non-admin user objects '''
        return filter(lambda user: user.has_permission('admin') is False, cls.all())

    @classmethod
    def by_id(cls, identifier):
        ''' Returns a the object with id of identifier '''
        return dbsession.query(cls).filter_by(id=identifier).first()

    @classmethod
    def by_uuid(cls, uuid):
        ''' Return and object based on a uuid '''
        return dbsession.query(cls).filter_by(uuid=unicode(uuid)).first()

    @classmethod
    def by_account(cls, account):
        ''' Return the user object whose user account is "account" '''
        return dbsession.query(cls).filter_by(account=unicode(account)).first()

    @classmethod
    def by_handle(cls, handle):
        ''' Return the user object whose user is "handle" '''
        return dbsession.query(cls).filter_by(handle=unicode(handle)).first()

    @classmethod
    def filter_string(cls, string, extra_chars=''):
        char_white_list = ascii_letters + digits + extra_chars
        return filter(lambda char: char in char_white_list, string)

    @classmethod
    def _hash_password(cls, algorithm_name, password, salt):
        '''
        Hashes the password using Md5/Sha1/Sha256/Scrypt; scrypt
        should only used for the admin accounts.

        @param algorithm_name: The hashing algorithm to be used
        @param password: Preimage to be hashed, non-ascii chars are ignored
        @param salt: Salt for password, only used with the scrypt algorithm
        @return: Unicode hexadecimal string of the hash digest
        @rtype: unicode
        '''
        password = filter(
            lambda char: char in printable[:-5], password
        ).encode('ascii')
        if algorithm_name == 'scrypt':
            return cls.__scrypt__(password, salt)
        elif algorithm_name in cls.algorithms:
            algo = cls.algorithms[algorithm_name]()
            algo.update(password)
            return unicode(algo.hexdigest())
        else:
            raise ValueError("Algorithm not supported.")

    @classmethod
    def __scrypt__(cls, password, salt):
        '''
        Uses scrypt to hash the password using a random salt

        @param password: The preimage to be hashed
        @param salt: The auto-generated hash
{'fraction_non_alphanumeric': 0.11717190052641133, 'fraction_numerical': 0.010891268832819023, 'mean_word_length': 3.162825840574235, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4773531', 'n_tokens_mistral': 3081, 'n_tokens_neox': 2965, 'n_words': 972}