123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- import hashlib, urllib
- from django.conf import settings
- from django.contrib.auth import hashers, models as auth_models
- from django.contrib.staticfiles.storage import staticfiles_storage
- from django.core import mail
- from django.test import client
- from django.utils import timezone
- from django.utils.translation import ugettext_lazy as _
- import mongoengine
- from mongoengine.django import auth
- from . import utils
- USERNAME_REGEX = r'[\w.@+-]+'
- CONFIRMATION_TOKEN_VALIDITY = 5 # days
- DEFAULT_USER_IMAGE = 'mongo_auth/images/unknown.png'
- # Used to reconstruct absolute/full URLs where request is not available
- DEFAULT_REQUEST = {
- 'SERVER_NAME': '127.0.0.1',
- 'SERVER_PORT': '8000',
- }
- class EmailConfirmationToken(mongoengine.EmbeddedDocument):
- value = mongoengine.StringField(max_length=20, required=True)
- created_time = mongoengine.DateTimeField(default=lambda: timezone.now(), required=True)
- def check_token(self, confirmation_token):
- if confirmation_token != self.value:
- return False
- elif (timezone.now() - self.created_time).days > CONFIRMATION_TOKEN_VALIDITY:
- return False
- else:
- return True
- class TwitterAccessToken(mongoengine.EmbeddedDocument):
- key = mongoengine.StringField(max_length=150)
- secret = mongoengine.StringField(max_length=150)
- class User(auth.User):
- username = mongoengine.StringField(
- max_length=30,
- min_length=4,
- regex=r'^' + USERNAME_REGEX + r'$',
- required=True,
- verbose_name=_("username"),
- help_text=_("Minimal of 4 characters and maximum of 30. Letters, digits and @/./+/-/_ only."),
- )
- lazyuser_username = mongoengine.BooleanField(default=True)
- facebook_access_token = mongoengine.StringField(max_length=255)
- facebook_profile_data = mongoengine.DictField()
- twitter_access_token = mongoengine.EmbeddedDocumentField(TwitterAccessToken)
- twitter_profile_data = mongoengine.DictField()
- google_access_token = mongoengine.StringField(max_length=150)
- google_profile_data = mongoengine.DictField()
- foursquare_access_token = mongoengine.StringField(max_length=150)
- foursquare_profile_data = mongoengine.DictField()
- browserid_profile_data = mongoengine.DictField()
- email_confirmed = mongoengine.BooleanField(default=False)
- email_confirmation_token = mongoengine.EmbeddedDocumentField(EmailConfirmationToken)
- @classmethod
- def get_initial_fields(cls, request):
- return {}
- def is_anonymous(self):
- return not self.is_authenticated()
- def is_authenticated(self):
- # TODO: Check if *_data fields are really false if not linked with third-party authentication
- return self.has_usable_password() or \
- self.facebook_profile_data or \
- self.twitter_profile_data or \
- self.google_profile_data or \
- self.foursquare_profile_data or \
- self.browserid_profile_data
- def check_password(self, raw_password):
- def setter(raw_password):
- self.set_password(raw_password)
- return hashers.check_password(raw_password, self.password, setter)
- def set_unusable_password(self):
- self.password = hashers.make_password(None)
- self.save()
- return self
- def has_usable_password(self):
- return hashers.is_password_usable(self.password)
- def email_user(self, subject, message, from_email=None, allow_unconfirmed=False):
- if not self.email:
- raise ValueError("Account e-mail address not set.")
- if not allow_unconfirmed and not self.email_confirmed:
- raise ValueError("Account e-mail address not confirmed.")
- mail.send_mail(subject, message, from_email, [self.email])
- def get_image_url(self):
- if self.twitter_profile_data and 'profile_image_url' in self.twitter_profile_data:
- return self.twitter_profile_data['profile_image_url']
- elif self.facebook_profile_data:
- # TODO: Do we really need this utils/graph_api_url?
- return '%s?type=square' % utils.graph_api_url('%s/picture' % self.username)
- elif self.foursquare_profile_data and 'photo' in self.foursquare_profile_data:
- return self.foursquare_profile_data['photo']
- elif self.google_profile_data and 'picture' in self.google_profile_data:
- return self.google_profile_data['picture']
- elif self.email:
- request = client.RequestFactory(**getattr(settings, 'DEFAULT_REQUEST', DEFAULT_REQUEST)).request()
- default_url = request.build_absolute_uri(staticfiles_storage.url(getattr(settings, 'DEFAULT_USER_IMAGE', DEFAULT_USER_IMAGE)))
- return 'https://secure.gravatar.com/avatar/%(email_hash)s?%(args)s' % {
- 'email_hash': hashlib.md5(self.email.lower()).hexdigest(),
- 'args': urllib.urlencode({
- 'default': default_url,
- 'size': 50,
- }),
- }
- else:
- return staticfiles_storage.url(getattr(settings, 'DEFAULT_USER_IMAGE', DEFAULT_USER_IMAGE))
- @classmethod
- def create_user(cls, username, email=None, password=None, lazyuser=False):
- now = timezone.now()
- if not username:
- raise ValueError("The given username must be set")
- email = auth_models.UserManager.normalize_email(email)
- user = cls(
- username=username,
- lazyuser_username=lazyuser,
- email=email or None,
- is_staff=False,
- is_active=True,
- is_superuser=False,
- last_login=now,
- date_joined=now,
- )
- user.set_password(password)
- user.save()
- return user
- def authenticate_facebook(self, request):
- if self.lazyuser_username and self.facebook_profile_data.get('username'):
- # TODO: Does Facebook have same restrictions on username content as we do?
- self.username = self.facebook_profile_data.get('username')
- self.lazyuser_username = False
- if self.first_name is None:
- self.first_name = self.facebook_profile_data.get('first_name') or None
- if self.last_name is None:
- self.last_name = self.facebook_profile_data.get('last_name') or None
- if self.email is None:
- # TODO: Do we know if all e-mail addresses given by Facebook are verified?
- # TODO: Does not Facebook support multiple e-mail addresses? Which one is given here?
- self.email = self.facebook_profile_data.get('email') or None
- def authenticate_twitter(self, request):
- if self.lazyuser_username and self.twitter_profile_data.get('screen_name'):
- # TODO: Does Twitter have same restrictions on username content as we do?
- self.username = self.twitter_profile_data.get('screen_name')
- self.lazyuser_username = False
- if self.first_name is None:
- self.first_name = self.twitter_profile_data.get('name') or None
- def authenticate_google(self, request):
- username_guess = self.google_profile_data.get('email', '').rsplit('@', 1)[0]
- if self.lazyuser_username and username_guess:
- # Best username guess we can get from Google OAuth
- self.username = username_guess
- self.lazyuser_username = False
- if self.first_name is None:
- self.first_name = self.google_profile_data.get('given_name') or None
- if self.last_name is None:
- self.last_name = self.google_profile_data.get('family_name') or None
- if self.email is None:
- self.email = self.google_profile_data.get('email') or None
- if self.google_profile_data.get('verified_email'):
- self.email_confirmed = True
- def authenticate_foursquare(self, request):
- username_guess = self.foursquare_profile_data.get('contact', {}).get('email', '').rsplit('@', 1)[0]
- if self.lazyuser_username and username_guess:
- # Best username guess we can get from Foursquare
- self.username = username_guess
- self.lazyuser_username = False
- if self.first_name is None:
- self.first_name = self.foursquare_profile_data.get('firstName') or None
- if self.last_name is None:
- self.last_name = self.foursquare_profile_data.get('lastName') or None
- if self.email is None:
- self.email = self.foursquare_profile_data.get('contact', {}).get('email') or None
- def authenticate_browserid(self, request):
- if self.lazyuser_username:
- # Best username guess we can get from BrowserID
- self.username = self.browserid_profile_data['email'].rsplit('@', 1)[0]
- self.lazyuser_username = False
- if self.email is None:
- self.email = self.browserid_profile_data['email'] or None
- # BrowserID takes care of email confirmation
- self.email_confirmed = True
- def authenticate_lazyuser(self, request):
- pass
|