Source

mon_django / mon_django / auth / models.py

Full commit
import datetime
import re

from django.utils.encoding import smart_str
from django.utils.hashcompat import md5_constructor, sha_constructor

from django.core import exceptions

from mon_django.core import get_db
from mon_django.core.fields import AutoField, DateTimeField
from mon_django.core.queryset import QuerySet

UNUSABLE_PASSWORD = '!' # This will never be a valid hash

def get_hexdigest(algorithm, salt, raw_password):
    """
    Returns a string of the hexdigest of the given plaintext password and salt
    using the given algorithm ('md5', 'sha1' or 'crypt').
    """
    raw_password, salt = smart_str(raw_password), smart_str(salt)
    if algorithm == 'crypt':
        try:
            import crypt
        except ImportError:
            raise ValueError('"crypt" password algorithm not supported in this environment')
        return crypt.crypt(raw_password, salt)

    if algorithm == 'md5':
        return md5_constructor(salt + raw_password).hexdigest()
    elif algorithm == 'sha1':
        return sha_constructor(salt + raw_password).hexdigest()
    raise ValueError("Got unknown password algorithm type in password.")


class UserManager(object):
    def __init__(self, model=None):
        if model:
            self.model = model

    def create(self, **kwargs):
        obj = self.model(**kwargs)
        obj.save()
        return obj

    def create_user(self, username, email, password=None):
        """
        Creates and saves a User with the given username, e-mail and password.
        """

        now = datetime.datetime.now()
        
        # Normalize the address by lowercasing the domain part of the email
        # address.
        try:
            email_name, domain_part = email.strip().split('@', 1)
        except ValueError:
            pass
        else:
            email = '@'.join([email_name, domain_part.lower()])

        user = self.model(username=username, email=email, is_staff=False,
                         is_active=True, is_superuser=False, last_login=now,
                         date_joined=now)

        if password:
            user.set_password(password)
        else:
            user.set_unusable_password()
        user.save()
        return user 

    def get_collection(self):
        collection = get_db()['mon_django__auth__users']
        collection.ensure_index('username', unique=True)
        return collection

    def count(self):
        return self.get_collection().count()

    def _get_query(self, **kwargs):
        query = {}

        for p, v in kwargs.iteritems():
            lst = p.split('__', 1)
            mod = ''
            if len(lst) > 1:
                p, mod = lst

            if p == 'pk' or p == 'id':
                p = '_id'

            if p in self.model.field_types:
                v = self.model.field_types[p].get_prep_value(v)

            if p in self.model.valid_fields:
                if not mod:
                    query[p] = v
                elif mod == 'iexact':
                    query[p] = re.compile(v, re.I)
                else:
                    raise TypeError("'%s' is an invalid modifier for this function" % mod)     
            else:
                raise TypeError("'%s' is an invalid keyword argument for this function" % p)     
        return query

    def get(self, **kwargs):
        query = self._get_query(**kwargs)
        result = self.get_collection().find(query)
        num = result.count()
        if num == 1:
            results = result[0]
            return User(**result[0])
        
        if not num:
            raise self.model.DoesNotExist("User matching query does not exist.") 

        raise self.model.MultipleObjectsReturned("get() returned more than one User -- it returned %s! Lookup parameters were %s"
                % (num, kwargs))

    def filter(self, **kwargs):
        query = self._get_query(**kwargs)
        result = self.get_collection().find(query)
        return QuerySet(self.model, result)

    def all(self):
        return self

class User(object):
    objects = UserManager()
    _default_manager = objects

    valid_fields = ('username', 'first_name', 'last_name', 'email', 'password',
                    'is_staff', 'is_active', 'is_superuser', 'last_login',
                    'date_joined', 'groups', 'user_permissions', '_id')

    valid_attrs = ('_params', 'backend')

    field_types = {
        'last_login' : DateTimeField(),
        '_id' : AutoField()
    }

    DoesNotExist = exceptions.ObjectDoesNotExist
    MultipleObjectsReturned = exceptions.MultipleObjectsReturned

    class Meta:
        object_name = 'User'
    _meta = Meta()

    def __init__(self, **kwargs):
        self._params = {}
        for p in kwargs.iterkeys():
            if p in self.valid_fields:
                if p in self.field_types:
                    self._params[p] = self.field_types[p].to_python(kwargs[p])
                else:
                    self._params[p] = kwargs[p]
            else:
                raise TypeError("'%s' is an invalid keyword argument for this function" % p) 

    def __getattr__(self, name):
        if name == 'id' or name == 'pk':
            name = '_id'
        if name in self.valid_attrs:
            return object.__getattr__(self, name)
        if not name in self.valid_fields:
            raise AttributeError("'User' object has no attribute '%s'" % name)
        try:
            return self._params[name]
        except KeyError:
            return '' # TODO: Create defaults.
        
    def __setattr__(self, name, value):
        if name == 'id' or name == 'pk':
            name = '_id'
        if name in self.valid_attrs:
            object.__setattr__(self, name, value)
            return
        if not name in self.valid_fields:
            raise AttributeError("'User' object has no attribute '%s'" % name)
        self._params[name] = value

    def get_profile(self):
        """
        Returns site-specific profile for this user. Raises
        SiteProfileNotAvailable if this site does not allow profiles.
        """
        if not hasattr(self, '_profile_cache'):
            from django.conf import settings
            if not getattr(settings, 'AUTH_PROFILE_MODULE', False):
                raise SiteProfileNotAvailable('You need to set AUTH_PROFILE_MO'
                                              'DULE in your project settings')
            try:
                app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
            except ValueError:
                raise SiteProfileNotAvailable('app_label and model_name should'
                        ' be separated by a dot in the AUTH_PROFILE_MODULE set'
                        'ting')

            try:
                from django.db import models
                model = models.get_model(app_label, model_name)
                if model is None:
                    raise SiteProfileNotAvailable('Unable to load the profile '
                        'model, check AUTH_PROFILE_MODULE in your project sett'
                        'ings')
                self._profile_cache = model._default_manager.using(self._state.db).get(user_id__exact=self.id)
                self._profile_cache.user = self
            except (ImportError, exceptions.ImproperlyConfigured):
                raise SiteProfileNotAvailable
        return self._profile_cache

    def set_password(self, raw_password):
        import random
        algo = 'sha1'
        salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
        hsh = get_hexdigest(algo, salt, raw_password)
        self.password = '%s$%s$%s' % (algo, salt, hsh)

    def save(self):
        self.objects.get_collection().save(self._params)

    def check_password(self, raw_password):
        """
        Returns a boolean of whether the raw_password was correct. Handles
        encryption formats behind the scenes.
        """
        # Backwards-compatibility check. Older passwords won't include the
        # algorithm or salt.
        if '$' not in self.password:
            is_correct = (self.password == get_hexdigest('md5', '', raw_password))
            if is_correct:
                # Convert the password to the new, more secure format.
                self.set_password(raw_password)
                self.save()
            return is_correct
        return check_password(raw_password, self.password)


UserManager.model = User

def check_password(raw_password, enc_password):
    """
    Returns a boolean of whether the raw_password was correct. Handles
    encryption formats behind the scenes.
    """
    algo, salt, hsh = enc_password.split('$')
    return hsh == get_hexdigest(algo, salt, raw_password)

class Group(object):
    pass

class Permission(object):
    pass

class SiteProfileNotAvailable(Exception):
    pass

class EmptyManager(UserManager):
    def get_query_set(self):
        return self.get_empty_query_set()

class AnonymousUser(object):
    id = None
    username = ''
    is_staff = False
    is_active = False
    is_superuser = False
    _groups = EmptyManager()
    _user_permissions = EmptyManager()

    def __init__(self):
        pass

    def __unicode__(self):
        return 'AnonymousUser'

    def __str__(self):
        return unicode(self).encode('utf-8')

    def __eq__(self, other):
        return isinstance(other, self.__class__)

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return 1 # instances always return the same hash value

    def save(self):
        raise NotImplementedError

    def delete(self):
        raise NotImplementedError

    def set_password(self, raw_password):
        raise NotImplementedError

    def check_password(self, raw_password):
        raise NotImplementedError

    def _get_groups(self):
        return self._groups
    groups = property(_get_groups)

    def _get_user_permissions(self):
        return self._user_permissions
    user_permissions = property(_get_user_permissions)

    def get_group_permissions(self, obj=None):
        return set()

    def get_all_permissions(self, obj=None):
        return _user_get_all_permissions(self, obj=obj)

    def has_perm(self, perm, obj=None):
        return _user_has_perm(self, perm, obj=obj)

    def has_perms(self, perm_list, obj=None):
        for perm in perm_list:
            if not self.has_perm(perm, obj):
                return False
        return True

    def has_module_perms(self, module):
        return _user_has_module_perms(self, module)

    def get_and_delete_messages(self):
        return []

    def is_anonymous(self):
        return True

    def is_authenticated(self):
        return False