Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Incorporates the ``delete_expired_users`` changes. #1

Open
wants to merge 3 commits into from

2 participants

@toastdriven

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.

@camilonova

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. @toastdriven
  2. @toastdriven

    Oh look, it's 2011.

    toastdriven authored
Commits on Jan 4, 2012
  1. @toastdriven
This page is out of date. Refresh to see the latest.
View
2  LICENSE
@@ -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
View
21 docs/backend-api.rst
@@ -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``.
View
21 docs/default-backend.rst
@@ -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
View
47 registration/backends/default/__init__.py
@@ -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()
View
3  registration/backends/simple/__init__.py
@@ -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
View
23 registration/management/commands/cleanupregistration.py
@@ -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()
View
109 registration/models.py
@@ -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)
-
+
View
29 registration/tests/backends.py
@@ -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):
View
19 registration/tests/models.py
@@ -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.