123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- from mongoengine import *
- from django.utils.encoding import smart_str
- from django.contrib.auth.models import _user_has_perm, _user_get_all_permissions, _user_has_module_perms
- from django.db import models
- from django.contrib.contenttypes.models import ContentTypeManager
- from django.contrib import auth
- from django.contrib.auth.models import AnonymousUser
- from django.utils.translation import ugettext_lazy as _
- from .utils import datetime_now
- REDIRECT_FIELD_NAME = 'next'
- try:
- from django.contrib.auth.hashers import check_password, make_password
- except ImportError:
- """Handle older versions of Django"""
- from django.utils.hashcompat import md5_constructor, sha_constructor
- def get_hexdigest(algorithm, salt, raw_password):
- raw_password, salt = smart_str(raw_password), smart_str(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')
- def check_password(raw_password, password):
- algo, salt, hash = password.split('$')
- return hash == get_hexdigest(algo, salt, raw_password)
- def make_password(raw_password):
- from random import random
- algo = 'sha1'
- salt = get_hexdigest(algo, str(random()), str(random()))[:5]
- hash = get_hexdigest(algo, salt, raw_password)
- return '%s$%s$%s' % (algo, salt, hash)
- class ContentType(Document):
- name = StringField(max_length=100)
- app_label = StringField(max_length=100)
- model = StringField(max_length=100, verbose_name=_('python model class name'),
- unique_with='app_label')
- objects = ContentTypeManager()
- class Meta:
- verbose_name = _('content type')
- verbose_name_plural = _('content types')
- # db_table = 'django_content_type'
- # ordering = ('name',)
- # unique_together = (('app_label', 'model'),)
- def __unicode__(self):
- return self.name
- def model_class(self):
- "Returns the Python model class for this type of content."
- from django.db import models
- return models.get_model(self.app_label, self.model)
- def get_object_for_this_type(self, **kwargs):
- """
- Returns an object of this type for the keyword arguments given.
- Basically, this is a proxy around this object_type's get_object() model
- method. The ObjectNotExist exception, if thrown, will not be caught,
- so code that calls this method should catch it.
- """
- return self.model_class()._default_manager.using(self._state.db).get(**kwargs)
- def natural_key(self):
- return (self.app_label, self.model)
- class SiteProfileNotAvailable(Exception):
- pass
- class PermissionManager(models.Manager):
- def get_by_natural_key(self, codename, app_label, model):
- return self.get(
- codename=codename,
- content_type=ContentType.objects.get_by_natural_key(app_label, model)
- )
- class Permission(Document):
- """The permissions system provides a way to assign permissions to specific
- users and groups of users.
- The permission system is used by the Django admin site, but may also be
- useful in your own code. The Django admin site uses permissions as follows:
- - The "add" permission limits the user's ability to view the "add"
- form and add an object.
- - The "change" permission limits a user's ability to view the change
- list, view the "change" form and change an object.
- - The "delete" permission limits the ability to delete an object.
- Permissions are set globally per type of object, not per specific object
- instance. It is possible to say "Mary may change news stories," but it's
- not currently possible to say "Mary may change news stories, but only the
- ones she created herself" or "Mary may only change news stories that have
- a certain status or publication date."
- Three basic permissions -- add, change and delete -- are automatically
- created for each Django model.
- """
- name = StringField(max_length=50, verbose_name=_('username'))
- content_type = ReferenceField(ContentType)
- codename = StringField(max_length=100, verbose_name=_('codename'))
- # FIXME: don't access field of the other class
- # unique_with=['content_type__app_label', 'content_type__model'])
- objects = PermissionManager()
- class Meta:
- verbose_name = _('permission')
- verbose_name_plural = _('permissions')
- # unique_together = (('content_type', 'codename'),)
- # ordering = ('content_type__app_label', 'content_type__model', 'codename')
- def __unicode__(self):
- return u"%s | %s | %s" % (
- unicode(self.content_type.app_label),
- unicode(self.content_type),
- unicode(self.name))
- def natural_key(self):
- return (self.codename,) + self.content_type.natural_key()
- natural_key.dependencies = ['contenttypes.contenttype']
- class Group(Document):
- """Groups are a generic way of categorizing users to apply permissions,
- or some other label, to those users. A user can belong to any number of
- groups.
- A user in a group automatically has all the permissions granted to that
- group. For example, if the group Site editors has the permission
- can_edit_home_page, any user in that group will have that permission.
- Beyond permissions, groups are a convenient way to categorize users to
- apply some label, or extended functionality, to them. For example, you
- could create a group 'Special users', and you could write code that would
- do special things to those users -- such as giving them access to a
- members-only portion of your site, or sending them members-only
- e-mail messages.
- """
- name = StringField(max_length=80, unique=True, verbose_name=_('name'))
- permissions = ListField(ReferenceField(Permission, verbose_name=_('permissions'), required=False))
- class Meta:
- verbose_name = _('group')
- verbose_name_plural = _('groups')
- def __unicode__(self):
- return self.name
- class UserManager(models.Manager):
- def create_user(self, username, email, password=None):
- """
- Creates and saves a User with the given username, e-mail and password.
- """
- now = 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)
- user.set_password(password)
- user.save(using=self._db)
- return user
- def create_superuser(self, username, email, password):
- u = self.create_user(username, email, password)
- u.is_staff = True
- u.is_active = True
- u.is_superuser = True
- u.save(using=self._db)
- return u
- def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):
- "Generates a random password with the given length and given allowed_chars"
- # Note that default value of allowed_chars does not have "I" or letters
- # that look like it -- just to avoid confusion.
- from random import choice
- return ''.join([choice(allowed_chars) for i in range(length)])
- class User(Document):
- """A User document that aims to mirror most of the API specified by Django
- at http://docs.djangoproject.com/en/dev/topics/auth/#users
- """
- username = StringField(max_length=30, required=True,
- verbose_name=_('username'),
- help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))
- first_name = StringField(max_length=30,
- verbose_name=_('first name'))
- last_name = StringField(max_length=30,
- verbose_name=_('last name'))
- email = EmailField(verbose_name=_('e-mail address'))
- password = StringField(max_length=128,
- verbose_name=_('password'),
- help_text=_("Use '[algo]$[iterations]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
- is_staff = BooleanField(default=False,
- verbose_name=_('staff status'),
- help_text=_("Designates whether the user can log into this admin site."))
- is_active = BooleanField(default=True,
- verbose_name=_('active'),
- help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts."))
- is_superuser = BooleanField(default=False,
- verbose_name=_('superuser status'),
- help_text=_("Designates that this user has all permissions without explicitly assigning them."))
- last_login = DateTimeField(default=datetime_now,
- verbose_name=_('last login'))
- date_joined = DateTimeField(default=datetime_now,
- verbose_name=_('date joined'))
- user_permissions = ListField(ReferenceField(Permission), verbose_name=_('user permissions'),
- help_text=_('Permissions for the user.'))
- USERNAME_FIELD = 'username'
- REQUIRED_FIELDS = ['email']
- meta = {
- 'allow_inheritance': True,
- 'indexes': [
- {'fields': ['username'], 'unique': True, 'sparse': True}
- ]
- }
- def __unicode__(self):
- return self.username
- def get_full_name(self):
- """Returns the users first and last names, separated by a space.
- """
- full_name = u'%s %s' % (self.first_name or '', self.last_name or '')
- return full_name.strip()
- def is_anonymous(self):
- return False
- def is_authenticated(self):
- return True
- def set_password(self, raw_password):
- """Sets the user's password - always use this rather than directly
- assigning to :attr:`~mongoengine.django.auth.User.password` as the
- password is hashed before storage.
- """
- self.password = make_password(raw_password)
- self.save()
- return self
- def check_password(self, raw_password):
- """Checks the user's password against a provided password - always use
- this rather than directly comparing to
- :attr:`~mongoengine.django.auth.User.password` as the password is
- hashed before storage.
- """
- return check_password(raw_password, self.password)
- @classmethod
- def create_user(cls, username, password, email=None):
- """Create (and save) a new user with the given username, password and
- email address.
- """
- now = datetime_now()
- # Normalize the address by lowercasing the domain part of the email
- # address.
- if email is not None:
- try:
- email_name, domain_part = email.strip().split('@', 1)
- except ValueError:
- pass
- else:
- email = '@'.join([email_name, domain_part.lower()])
- user = cls(username=username, email=email, date_joined=now)
- user.set_password(password)
- user.save()
- return user
- def get_group_permissions(self, obj=None):
- """
- Returns a list of permission strings that this user has through his/her
- groups. This method queries all available auth backends. If an object
- is passed in, only permissions matching this object are returned.
- """
- permissions = set()
- for backend in auth.get_backends():
- if hasattr(backend, "get_group_permissions"):
- permissions.update(backend.get_group_permissions(self, obj))
- return permissions
- def get_all_permissions(self, obj=None):
- return _user_get_all_permissions(self, obj)
- def has_perm(self, perm, obj=None):
- """
- Returns True if the user has the specified permission. This method
- queries all available auth backends, but returns immediately if any
- backend returns True. Thus, a user who has permission from a single
- auth backend is assumed to have permission in general. If an object is
- provided, permissions for this specific object are checked.
- """
- # Active superusers have all permissions.
- if self.is_active and self.is_superuser:
- return True
- # Otherwise we need to check the backends.
- return _user_has_perm(self, perm, obj)
- def has_module_perms(self, app_label):
- """
- Returns True if the user has any permissions in the given app label.
- Uses pretty much the same logic as has_perm, above.
- """
- # Active superusers have all permissions.
- if self.is_active and self.is_superuser:
- return True
- return _user_has_module_perms(self, app_label)
- def email_user(self, subject, message, from_email=None):
- "Sends an e-mail to this User."
- from django.core.mail import send_mail
- send_mail(subject, message, from_email, [self.email])
- 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:
- 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, ImproperlyConfigured):
- raise SiteProfileNotAvailable
- return self._profile_cache
- class MongoEngineBackend(object):
- """Authenticate using MongoEngine and mongoengine.django.auth.User.
- """
- supports_object_permissions = False
- supports_anonymous_user = False
- supports_inactive_user = False
- _user_doc = False
- def authenticate(self, username=None, password=None):
- user = self.user_document.objects(username=username).first()
- if user:
- if password and user.check_password(password):
- backend = auth.get_backends()[0]
- user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
- return user
- return None
- def get_user(self, user_id):
- return self.user_document.objects.with_id(user_id)
- @property
- def user_document(self):
- if self._user_doc is False:
- from .mongo_auth.models import get_user_document
- self._user_doc = get_user_document()
- return self._user_doc
- def get_user(userid):
- """Returns a User object from an id (User.id). Django's equivalent takes
- request, but taking an id instead leaves it up to the developer to store
- the id in any way they want (session, signed cookie, etc.)
- """
- if not userid:
- return AnonymousUser()
- return MongoEngineBackend().get_user(userid) or AnonymousUser()
|