PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/registration/models.py

http://vyperblog.googlecode.com/
Python | 300 lines | 290 code | 8 blank | 2 comment | 9 complexity | bd24c1591f067b942c6edded71ef1376 MD5 | raw file
Possible License(s): LGPL-2.0
  1. import datetime
  2. import random
  3. import re
  4. import sha
  5. import logging
  6. from google.appengine.ext import db
  7. from django.conf import settings
  8. from django.contrib.auth.models import User
  9. from django.contrib.sites.models import Site
  10. from django.db import models
  11. from django.utils.translation import ugettext_lazy as _
  12. from django.template import Context
  13. from vyperlogix.django import django_utils
  14. from vyperlogix.misc import _utils
  15. __registration_activation_email_subject__ = '''Activation of your User Account for {{ site }} '''
  16. __registration_activation_email__ = '''
  17. Welcome to VyperBlog !
  18. In order to activate your account please visit the following link:
  19. https://{{ site }}{% url registration_activate activation_key=activation_key %}
  20. Thanks,
  21. Your {{ site }} Web Team
  22. '''
  23. __account_password_email_subject__ = '''User Account Password Change for {{ site }} '''
  24. __account_password_email__ = '''
  25. Welcome to VyperBlog !
  26. In order to change your account password please visit the following link:
  27. https://{{ site }}/account/password/{{ email_address }}/
  28. Thanks,
  29. Your {{ site }} Web Team
  30. '''
  31. SHA1_RE = re.compile('^[a-f0-9]{40}$')
  32. class RegistrationManager(models.Manager):
  33. """
  34. Custom manager for the ``RegistrationProfile`` model.
  35. The methods defined here provide shortcuts for account creation
  36. and activation (including generation and emailing of activation
  37. keys), and for cleaning out expired inactive accounts.
  38. """
  39. def activate_user(self, activation_key):
  40. """
  41. Validate an activation key and activate the corresponding
  42. ``User`` if valid.
  43. If the key is valid and has not expired, return the ``User``
  44. after activating.
  45. If the key is not valid or has expired, return ``False``.
  46. If the key is valid but the ``User`` is already active,
  47. return ``False``.
  48. To prevent reactivation of an account which has been
  49. deactivated by site administrators, the activation key is
  50. reset to the string constant ``RegistrationProfile.ACTIVATED``
  51. after successful activation.
  52. To execute customized logic when a ``User`` is activated,
  53. connect a function to the signal
  54. ``registration.signals.user_activated``; this signal will be
  55. sent (with the ``User`` as the value of the keyword argument
  56. ``user``) after a successful activation.
  57. """
  58. from registration.signals import user_activated
  59. # Make sure the key we're trying conforms to the pattern of a
  60. # SHA1 hash; if it doesn't, no point trying to look it up in
  61. # the database.
  62. activation_key_expired = True
  63. if SHA1_RE.search(activation_key):
  64. profile = RegistrationProfile.get_by_key_name("key_"+activation_key)
  65. if (not profile):
  66. registrations = [aRegistration for aRegistration in RegistrationProfile.all() if (aRegistration.activation_key == activation_key)]
  67. if (len(registrations) > 0):
  68. profile = registrations[0]
  69. activation_key_expired = False
  70. else:
  71. activation_key_expired = profile.activation_key_expired()
  72. if not profile:
  73. return False
  74. if not activation_key_expired:
  75. user = profile.user
  76. user.is_active = True
  77. user.put()
  78. profile.activation_key = RegistrationProfile.ACTIVATED
  79. profile.put()
  80. user_activated.send(sender=self.model, user=user)
  81. return user
  82. return False
  83. def create_inactive_user(self, username, password, email, first_name, last_name, domain_override="", send_email=True):
  84. """
  85. Create a new, inactive ``User``, generate a
  86. ``RegistrationProfile`` and email its activation key to the
  87. ``User``, returning the new ``User``.
  88. To disable the email, call with ``send_email=False``.
  89. The activation email will make use of two templates:
  90. ``registration/activation_email_subject.txt``
  91. This template will be used for the subject line of the
  92. email. It receives one context variable, ``site``, which
  93. is the currently-active
  94. ``django.contrib.sites.models.Site`` instance. Because it
  95. is used as the subject line of an email, this template's
  96. output **must** be only a single line of text; output
  97. longer than one line will be forcibly joined into only a
  98. single line.
  99. ``registration/activation_email.txt``
  100. This template will be used for the body of the email. It
  101. will receive three context variables: ``activation_key``
  102. will be the user's activation key (for use in constructing
  103. a URL to activate the account), ``expiration_days`` will
  104. be the number of days for which the key will be valid and
  105. ``site`` will be the currently-active
  106. ``django.contrib.sites.models.Site`` instance.
  107. To execute customized logic once the new ``User`` has been
  108. created, connect a function to the signal
  109. ``registration.signals.user_registered``; this signal will be
  110. sent (with the new ``User`` as the value of the keyword
  111. argument ``user``) after the ``User`` and
  112. ``RegistrationProfile`` have been created, and the email (if
  113. any) has been sent..
  114. """
  115. from registration.signals import user_registered
  116. # prepend "key_" to the key_name, because key_names can't start with numbers
  117. new_user = User(username=username, key_name="key_"+username.lower(), email=email, first_name=first_name, last_name=last_name, is_active=False)
  118. new_user.set_password(password)
  119. new_user.put()
  120. registration_profile = self.create_profile(new_user)
  121. if send_email:
  122. from django.core.mail import send_mail
  123. current_site = domain_override
  124. #current_site = Site.objects.get_current()
  125. subject = django_utils.render_from_string(__registration_activation_email_subject__,context=Context({ 'site': current_site },autoescape=False))
  126. subject = ''.join(subject.splitlines()) # Email subject *must not* contain newlines
  127. c = { 'activation_key': registration_profile.activation_key,
  128. 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
  129. 'site': current_site
  130. }
  131. context = Context(c,autoescape=False)
  132. message = django_utils.render_from_string(__registration_activation_email__,context=context)
  133. try:
  134. send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [new_user.email])
  135. except Exception, e:
  136. info_string = _utils.formattedException(details=e)
  137. logging.error('ResendRegistrationForm.save.ERROR --> %s' % (info_string))
  138. user_registered.send(sender=self.model, user=new_user)
  139. return new_user
  140. def create_profile(self, user):
  141. """
  142. Create a ``RegistrationProfile`` for a given
  143. ``User``, and return the ``RegistrationProfile``.
  144. The activation key for the ``RegistrationProfile`` will be a
  145. SHA1 hash, generated from a combination of the ``User``'s
  146. username and a random salt.
  147. """
  148. salt = sha.new(str(random.random())).hexdigest()[:5]
  149. activation_key = sha.new(salt+user.username).hexdigest()
  150. # prepend "key_" to the key_name, because key_names can't start with numbers
  151. registrationprofile = RegistrationProfile(user=user, activation_key=activation_key, key_name="key_"+activation_key)
  152. registrationprofile.put()
  153. return registrationprofile
  154. def delete_expired_users(self):
  155. """
  156. Remove expired instances of ``RegistrationProfile`` and their
  157. associated ``User``s.
  158. Accounts to be deleted are identified by searching for
  159. instances of ``RegistrationProfile`` with expired activation
  160. keys, and then checking to see if their associated ``User``
  161. instances have the field ``is_active`` set to ``False``; any
  162. ``User`` who is both inactive and has an expired activation
  163. key will be deleted.
  164. It is recommended that this method be executed regularly as
  165. part of your routine site maintenance; this application
  166. provides a custom management command which will call this
  167. method, accessible as ``manage.py cleanupregistration``.
  168. Regularly clearing out accounts which have never been
  169. activated serves two useful purposes:
  170. 1. It alleviates the ocasional need to reset a
  171. ``RegistrationProfile`` and/or re-send an activation email
  172. when a user does not receive or does not act upon the
  173. initial activation email; since the account will be
  174. deleted, the user will be able to simply re-register and
  175. receive a new activation key.
  176. 2. It prevents the possibility of a malicious user registering
  177. one or more accounts and never activating them (thus
  178. denying the use of those usernames to anyone else); since
  179. those accounts will be deleted, the usernames will become
  180. available for use again.
  181. If you have a troublesome ``User`` and wish to disable their
  182. account while keeping it in the database, simply delete the
  183. associated ``RegistrationProfile``; an inactive ``User`` which
  184. does not have an associated ``RegistrationProfile`` will not
  185. be deleted.
  186. """
  187. for profile in RegistrationProfile.all():
  188. if profile.activation_key_expired():
  189. user = profile.user
  190. if not user.is_active:
  191. user.delete()
  192. profile.delete()
  193. class RegistrationProfile(db.Model):
  194. """
  195. A simple profile which stores an activation key for use during
  196. user account registration.
  197. Generally, you will not want to interact directly with instances
  198. of this model; the provided manager includes methods
  199. for creating and activating new accounts, as well as for cleaning
  200. out accounts which have never been activated.
  201. While it is possible to use this model as the value of the
  202. ``AUTH_PROFILE_MODULE`` setting, it's not recommended that you do
  203. so. This model's sole purpose is to store data temporarily during
  204. account registration and activation.
  205. """
  206. ACTIVATED = u"ALREADY_ACTIVATED"
  207. user = db.ReferenceProperty(User, verbose_name=_('user'))
  208. activation_key = db.StringProperty(_('activation key'))
  209. objects = RegistrationManager()
  210. timestamp = db.DateTimeProperty(auto_now=True)
  211. class Meta:
  212. verbose_name = _('registration profile')
  213. verbose_name_plural = _('registration profiles')
  214. def __unicode__(self):
  215. return u"Registration information for %s (%s) (%s) %s" % (self.user.username,self.user.email,_utils.getAsSimpleDateStr(self.timestamp,str(_utils.formatDjangoDateTimeStr())),self.activation_key)
  216. def activation_key_expired(self):
  217. """
  218. Determine whether this ``RegistrationProfile``'s activation
  219. key has expired, returning a boolean -- ``True`` if the key
  220. has expired.
  221. Key expiration is determined by a two-step process:
  222. 1. If the user has already activated, the key will have been
  223. reset to the string constant ``ACTIVATED``. Re-activating
  224. is not permitted, and so this method returns ``True`` in
  225. this case.
  226. 2. Otherwise, the date the user signed up is incremented by
  227. the number of days specified in the setting
  228. ``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of
  229. days after signup during which a user is allowed to
  230. activate their account); if the result is less than or
  231. equal to the current date, the key has expired and this
  232. method returns ``True``.
  233. """
  234. expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
  235. return self.activation_key == RegistrationProfile.ACTIVATED or \
  236. (self.user.date_joined + expiration_date <= datetime.datetime.now())
  237. activation_key_expired.boolean = True