Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Incorporates the ``delete_expired_users`` changes. #1

Open
wants to merge 3 commits into from

2 participants

Daniel Lindsley Camilo Nova
Daniel Lindsley

No more data loss. ubernostrum has a pull request in his BB of the same patch, which he previously agreed to apply, so hopefully the final commit looks similar when he gets around to it.

Camilo Nova

You should merge this changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 27, 2011
  1. Daniel Lindsley
  2. Daniel Lindsley

    Oh look, it's 2011.

    toastdriven authored
Commits on Jan 4, 2012
  1. Daniel Lindsley
This page is out of date. Refresh to see the latest.
2  LICENSE
View
@@ -1,4 +1,4 @@
-Copyright (c) 2007-2010, James Bennett
+Copyright (c) 2007-2011, James Bennett
All rights reserved.
Redistribution and use in source and binary forms, with or without
21 docs/backend-api.rst
View
@@ -228,3 +228,24 @@ Arguments to this method are:
For workflows which do not require a separate activation step, this
method can and should raise ``NotImplementedError``.
+
+
+delete_expired_users()
+~~~~~~~~~~~~~~~~~~~~~~
+
+Removes expired instances of :class:`RegistrationProfile`, and
+their associated user accounts, from the database. This is
+useful as a periodic maintenance task to clean out accounts
+which registered but never activated.
+
+Accounts to be deleted are identified by searching for instances
+of :class:`RegistrationProfile` with expired activation keys and
+with associated user accounts which are inactive (have their
+``is_active`` field set to ``False``). To disable a user account
+without having it deleted, simply delete its associated
+:class:`RegistrationProfile`; any ``User`` which does not have
+an associated :class:`RegistrationProfile` will not be deleted.
+
+A custom management command is provided which will execute this
+method, suitable for use in cron jobs or other scheduled
+maintenance tasks: ``manage.py cleanupregistration``.
21 docs/default-backend.rst
View
@@ -178,27 +178,6 @@ Additionally, :class:`RegistrationProfile` has a custom manager
:type activation_key: string, a 40-character SHA1 hexdigest
:rtype: ``User`` or bool
- .. method:: delete_expired_users
-
- Removes expired instances of :class:`RegistrationProfile`, and
- their associated user accounts, from the database. This is
- useful as a periodic maintenance task to clean out accounts
- which registered but never activated.
-
- Accounts to be deleted are identified by searching for instances
- of :class:`RegistrationProfile` with expired activation keys and
- with associated user accounts which are inactive (have their
- ``is_active`` field set to ``False``). To disable a user account
- without having it deleted, simply delete its associated
- :class:`RegistrationProfile`; any ``User`` which does not have
- an associated :class:`RegistrationProfile` will not be deleted.
-
- A custom management command is provided which will execute this
- method, suitable for use in cron jobs or other scheduled
- maintenance tasks: ``manage.py cleanupregistration``.
-
- :rtype: ``None``
-
.. method:: create_inactive_user(username, email, password, site[, send_email])
Creates a new, inactive user account and an associated instance
47 registration/backends/default/__init__.py
View
@@ -137,3 +137,50 @@ def post_activation_redirect(self, request, user):
"""
return ('registration_activation_complete', (), {})
+
+ def delete_expired_users(self):
+ """
+ Remove expired instances of ``RegistrationProfile`` and their
+ associated ``User``s.
+
+ Accounts to be deleted are identified by searching for
+ instances of ``RegistrationProfile`` with expired activation
+ keys, and then checking to see if their associated ``User``
+ instances have the field ``is_active`` set to ``False``; any
+ ``User`` who is both inactive and has an expired activation
+ key will be deleted.
+
+ It is recommended that this method be executed regularly as
+ part of your routine site maintenance; this application
+ provides a custom management command which will call this
+ method, accessible as ``manage.py cleanupregistration``.
+
+ Regularly clearing out accounts which have never been
+ activated serves two useful purposes:
+
+ 1. It alleviates the ocasional need to reset a
+ ``RegistrationProfile`` and/or re-send an activation email
+ when a user does not receive or does not act upon the
+ initial activation email; since the account will be
+ deleted, the user will be able to simply re-register and
+ receive a new activation key.
+
+ 2. It prevents the possibility of a malicious user registering
+ one or more accounts and never activating them (thus
+ denying the use of those usernames to anyone else); since
+ those accounts will be deleted, the usernames will become
+ available for use again.
+
+ This method respects inactive ``User``s whose associated
+ ``RegistrationProfile`` has been marked as activated. In this case,
+ only the ``RegistrationProfile`` will be deleted, leaving the inactive
+ ``User`` (and related data) in place.
+
+ """
+ for profile in RegistrationProfile.objects.all():
+ if profile.is_activated():
+ profile.delete()
+ elif profile.activation_key_expired():
+ user = profile.user
+ if not user.is_active:
+ user.delete()
3  registration/backends/simple/__init__.py
View
@@ -4,10 +4,11 @@
from django.contrib.auth.models import User
from registration import signals
+from registration.backends.default import DefaultBackend
from registration.forms import RegistrationForm
-class SimpleBackend(object):
+class SimpleBackend(DefaultBackend):
"""
A registration backend which implements the simplest possible
workflow: a user supplies a username, email address and password
23 registration/management/commands/cleanupregistration.py
View
@@ -2,18 +2,31 @@
A management command which deletes expired accounts (e.g.,
accounts which signed up but never activated) from the database.
-Calls ``RegistrationProfile.objects.delete_expired_users()``, which
-contains the actual logic for determining which accounts are deleted.
+Calls ``backend.delete_expired_users()``, which contains the actual
+logic for determining which accounts are deleted.
+
+You can supply a custom backend using the ``--backend`` flag, which
+takes a dotted import path to the appropriate class.
"""
+from optparse import make_option
+
from django.core.management.base import NoArgsCommand
-from registration.models import RegistrationProfile
+from registration.backends import get_backend
class Command(NoArgsCommand):
help = "Delete expired user registrations from the database"
+ option_list = NoArgsCommand.option_list + (
+ make_option('--backend',
+ action='store',
+ dest='backend',
+ default='registration.backends.default.DefaultBackend',
+ help='Specifies the backend to use when removing expired accounts'),
+ )
- def handle_noargs(self, **options):
- RegistrationProfile.objects.delete_expired_users()
+ def handle(self, **options):
+ backend = get_backend(options['backend'])
+ backend.delete_expired_users()
109 registration/models.py
View
@@ -17,25 +17,25 @@
class RegistrationManager(models.Manager):
"""
Custom manager for the ``RegistrationProfile`` model.
-
+
The methods defined here provide shortcuts for account creation
and activation (including generation and emailing of activation
keys), and for cleaning out expired inactive accounts.
-
+
"""
def activate_user(self, activation_key):
"""
Validate an activation key and activate the corresponding
``User`` if valid.
-
+
If the key is valid and has not expired, return the ``User``
after activating.
-
+
If the key is not valid or has expired, return ``False``.
-
+
If the key is valid but the ``User`` is already active,
return ``False``.
-
+
To prevent reactivation of an account which has been
deactivated by site administrators, the activation key is
reset to the string constant ``RegistrationProfile.ACTIVATED``
@@ -58,7 +58,7 @@ def activate_user(self, activation_key):
profile.save()
return user
return False
-
+
def create_inactive_user(self, username, email, password,
site, send_email=True):
"""
@@ -68,7 +68,7 @@ def create_inactive_user(self, username, email, password,
By default, an activation email will be sent to the new
user. To disable this, pass ``send_email=False``.
-
+
"""
new_user = User.objects.create_user(username, email, password)
new_user.is_active = False
@@ -86,11 +86,11 @@ def create_profile(self, user):
"""
Create a ``RegistrationProfile`` for a given
``User``, and return the ``RegistrationProfile``.
-
+
The activation key for the ``RegistrationProfile`` will be a
SHA1 hash, generated from a combination of the ``User``'s
username and a random salt.
-
+
"""
salt = sha_constructor(str(random.random())).hexdigest()[:5]
username = user.username
@@ -99,92 +99,52 @@ def create_profile(self, user):
activation_key = sha_constructor(salt+username).hexdigest()
return self.create(user=user,
activation_key=activation_key)
-
+
def delete_expired_users(self):
- """
- Remove expired instances of ``RegistrationProfile`` and their
- associated ``User``s.
-
- Accounts to be deleted are identified by searching for
- instances of ``RegistrationProfile`` with expired activation
- keys, and then checking to see if their associated ``User``
- instances have the field ``is_active`` set to ``False``; any
- ``User`` who is both inactive and has an expired activation
- key will be deleted.
-
- It is recommended that this method be executed regularly as
- part of your routine site maintenance; this application
- provides a custom management command which will call this
- method, accessible as ``manage.py cleanupregistration``.
-
- Regularly clearing out accounts which have never been
- activated serves two useful purposes:
-
- 1. It alleviates the ocasional need to reset a
- ``RegistrationProfile`` and/or re-send an activation email
- when a user does not receive or does not act upon the
- initial activation email; since the account will be
- deleted, the user will be able to simply re-register and
- receive a new activation key.
-
- 2. It prevents the possibility of a malicious user registering
- one or more accounts and never activating them (thus
- denying the use of those usernames to anyone else); since
- those accounts will be deleted, the usernames will become
- available for use again.
-
- If you have a troublesome ``User`` and wish to disable their
- account while keeping it in the database, simply delete the
- associated ``RegistrationProfile``; an inactive ``User`` which
- does not have an associated ``RegistrationProfile`` will not
- be deleted.
-
- """
- for profile in self.all():
- if profile.activation_key_expired():
- user = profile.user
- if not user.is_active:
- user.delete()
+ raise DeprecationWarning("This method has been removed. Please use the 'delete_expired_users' on the correct backend.")
class RegistrationProfile(models.Model):
"""
A simple profile which stores an activation key for use during
user account registration.
-
+
Generally, you will not want to interact directly with instances
of this model; the provided manager includes methods
for creating and activating new accounts, as well as for cleaning
out accounts which have never been activated.
-
+
While it is possible to use this model as the value of the
``AUTH_PROFILE_MODULE`` setting, it's not recommended that you do
so. This model's sole purpose is to store data temporarily during
account registration and activation.
-
+
"""
ACTIVATED = u"ALREADY_ACTIVATED"
-
+
user = models.ForeignKey(User, unique=True, verbose_name=_('user'))
activation_key = models.CharField(_('activation key'), max_length=40)
-
+
objects = RegistrationManager()
-
+
class Meta:
verbose_name = _('registration profile')
verbose_name_plural = _('registration profiles')
-
+
def __unicode__(self):
return u"Registration information for %s" % self.user
-
+
+ def is_activated(self):
+ return self.activation_key == self.ACTIVATED
+
def activation_key_expired(self):
"""
Determine whether this ``RegistrationProfile``'s activation
key has expired, returning a boolean -- ``True`` if the key
has expired.
-
+
Key expiration is determined by a two-step process:
-
+
1. If the user has already activated, the key will have been
reset to the string constant ``ACTIVATED``. Re-activating
is not permitted, and so this method returns ``True`` in
@@ -197,18 +157,23 @@ def activation_key_expired(self):
activate their account); if the result is less than or
equal to the current date, the key has expired and this
method returns ``True``.
-
+
"""
expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
- return self.activation_key == self.ACTIVATED or \
- (self.user.date_joined + expiration_date <= datetime.datetime.now())
+ if getattr(settings, 'USE_TZ', False):
+ from django.utils import timezone
+ now = timezone.now()
+ else:
+ now = datetime.datetime.now()
+ return self.is_activated() or \
+ (self.user.date_joined + expiration_date <= now)
activation_key_expired.boolean = True
def send_activation_email(self, site):
"""
Send an activation email to the user associated with this
``RegistrationProfile``.
-
+
The activation email will make use of two templates:
``registration/activation_email_subject.txt``
@@ -249,9 +214,9 @@ def send_activation_email(self, site):
ctx_dict)
# Email subject *must not* contain newlines
subject = ''.join(subject.splitlines())
-
+
message = render_to_string('registration/activation_email.txt',
ctx_dict)
-
+
self.user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
-
+
29 registration/tests/backends.py
View
@@ -367,6 +367,35 @@ def test_activation_action(self):
admin_class.activate_users(_mock_request(),
RegistrationProfile.objects.all())
self.failUnless(User.objects.get(username='alice').is_active)
+
+ def test_expired_user_deletion(self):
+ """
+ ``DefaultBackend.delete_expired_users()`` only
+ deletes inactive users whose activation window has expired.
+
+ """
+ new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(),
+ username='alice',
+ password='swordfish',
+ email='alice@example.com')
+ expired_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(),
+ username='bob',
+ password='secret',
+ email='bob@example.com')
+ expired_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1)
+ expired_user.save()
+ disabled_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(),
+ username='disabled',
+ password='password',
+ email='disabled@example.com')
+ disabled_user.activation_key = RegistrationProfile.ACTIVATED
+ disabled_user.save()
+
+ self.assertRaises(DeprecationWarning, RegistrationProfile.objects.delete_expired_users)
+ self.assertEqual(RegistrationProfile.objects.count(), 3)
+ self.backend.delete_expired_users()
+ self.assertEqual(RegistrationProfile.objects.count(), 2)
+ self.assertRaises(User.DoesNotExist, User.objects.get, username='bob')
class SimpleRegistrationBackendTests(TestCase):
19 registration/tests/models.py
View
@@ -186,25 +186,6 @@ def test_activation_nonexistent_key(self):
invalid_key = sha_constructor('foo').hexdigest()
self.failIf(RegistrationProfile.objects.activate_user(invalid_key))
- def test_expired_user_deletion(self):
- """
- ``RegistrationProfile.objects.delete_expired_users()`` only
- deletes inactive users whose activation window has expired.
-
- """
- new_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(),
- **self.user_info)
- expired_user = RegistrationProfile.objects.create_inactive_user(site=Site.objects.get_current(),
- username='bob',
- password='secret',
- email='bob@example.com')
- expired_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1)
- expired_user.save()
-
- RegistrationProfile.objects.delete_expired_users()
- self.assertEqual(RegistrationProfile.objects.count(), 1)
- self.assertRaises(User.DoesNotExist, User.objects.get, username='bob')
-
def test_management_command(self):
"""
The ``cleanupregistration`` management command properly
Something went wrong with that request. Please try again.