Skip to content

Commit

Permalink
Add tests for custom users, update Travis test config, and add docs.
Browse files Browse the repository at this point in the history
Adds new tests for Django 1.5's new custom user models, and updates
the implementation to support them (there's an issue with the original
code where it didn't pick up on an updated AUTH_USER_MODEL setting).

Updates .travis.yml to test Django 1.3, 1.4, and 1.5.

Adds documentation to help users customize django-browserid for their
custom user models.
  • Loading branch information
Michael Kelly committed Nov 7, 2012
1 parent 9ba5897 commit a6b06b5
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 9 deletions.
10 changes: 7 additions & 3 deletions .travis.yml
Expand Up @@ -2,7 +2,11 @@ language: python
python:
- 2.6
- 2.7
# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
install: pip install -r requirements.txt
# command to run tests, e.g. python setup.py test
env:
- DJANGO_VERSION=1.3.4
- DJANGO_VERSION=1.4.2
- DJANGO_VERSION=1.5a1
install:
- pip install -e git+git://github.com/django/django.git@${DJANGO_VERSION}#egg=django
- pip install -r requirements.txt --use-mirrors
script: fab test
23 changes: 17 additions & 6 deletions django_browserid/auth.py
Expand Up @@ -17,10 +17,12 @@

try:
from django.contrib.auth import get_user_model
User = get_user_model()
except ImportError:
from django.contrib.auth.models import User

def get_user_model(*args, **kwargs):
return User


log = logging.getLogger(__name__)

Expand All @@ -45,14 +47,22 @@ class BrowserIDBackend(object):
supports_inactive_user = True
supports_object_permissions = False

def __init__(self):
"""
Store the current user model on creation to avoid issues if
settings.AUTH_USER_MODEL changes, which usually only happens during
tests.
"""
self.User = get_user_model()

def verify(self, *args):
warn('Deprecated, please use the standalone function '
'django_browserid.verify instead.', DeprecationWarning)
return verify(*args)

def filter_users_by_email(self, email):
"""Return all users matching the specified email."""
return User.objects.filter(email=email)
return self.User.objects.filter(email=email)

def create_user(self, email):
"""Return object for a newly created user account."""
Expand All @@ -62,7 +72,7 @@ def create_user(self, email):
else:
username = default_username_algo(email)

return User.objects.create_user(username, email)
return self.User.objects.create_user(username, email)

def authenticate(self, assertion=None, audience=None):
"""``django.contrib.auth`` compatible authentication method.
Expand All @@ -86,7 +96,8 @@ def authenticate(self, assertion=None, audience=None):
# log and bail. randomly selecting one seems really wrong.
users = self.filter_users_by_email(email=email)
if len(users) > 1:
log.warn('{0} users with email address {1}.'.format(len(users), email))
log.warn('{0} users with email address {1}.'.format(len(users),
email))
return None
if len(users) == 1:
return users[0]
Expand All @@ -107,8 +118,8 @@ def authenticate(self, assertion=None, audience=None):

def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return self.User.objects.get(pk=user_id)
except self.User.DoesNotExist:
return None

def _load_module(self, path):
Expand Down
23 changes: 23 additions & 0 deletions django_browserid/tests/models.py
@@ -0,0 +1,23 @@
"""
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
from django.db import models

try:
from django.contrib.auth.models import AbstractBaseUser
except ImportError:
AbstractBaseUser = object


class CustomUser(AbstractBaseUser):
USERNAME_FIELD = 'email'

email = models.EmailField(unique=True, db_index=True)

def get_full_name(self):
return self.email

def get_short_name(self):
return self.email
3 changes: 3 additions & 0 deletions django_browserid/tests/settings.py
Expand Up @@ -5,6 +5,8 @@
"""
TEST_RUNNER = 'django_nose.runner.NoseTestSuiteRunner'

SECRET_KEY = 'asdf'

DATABASES = {
'default': {
'NAME': 'test.db',
Expand All @@ -15,6 +17,7 @@
INSTALLED_APPS = (
'django_nose',
'django_browserid',
'django_browserid.tests',

'django.contrib.auth',
'django.contrib.contenttypes',
Expand Down
39 changes: 39 additions & 0 deletions django_browserid/tests/test_auth.py
Expand Up @@ -12,6 +12,12 @@
from django_browserid.auth import BrowserIDBackend, default_username_algo
from django_browserid.tests import mock_browserid

try:
from django.contrib.auth import get_user_model
from django_browserid.tests.models import CustomUser
except ImportError:
get_user_model = False


def new_user(email, username=None):
"""Creates a user with the specified email for testing."""
Expand Down Expand Up @@ -87,3 +93,36 @@ def test_user_created_signal(self, user_created):
# created.
user = self.auth('a@b.com')
assert user_created.call.called_with(user=user)


# Only run custom user model tests if we're using a version of Django that
# supports it.
if get_user_model:
@patch.object(settings, 'AUTH_USER_MODEL', 'tests.CustomUser')
class CustomUserModelTests(TestCase):
def _auth(self, backend=None, verified_email=None):
if backend is None:
backend = BrowserIDBackend()

with mock_browserid(verified_email):
return backend.authenticate(assertion='asdf', audience='asdf')

def test_existing_user(self):
"""If a custom user exists with the given email, return them."""
user = CustomUser.objects.create(email='a@test.com')
authed_user = self._auth(verified_email='a@test.com')
assert user == authed_user

@patch.object(settings, 'BROWSERID_CREATE_USER', True)
def test_create_new_user(self):
"""
If a custom user does not exist with the given email, create a new
user and return them.
"""
class CustomUserBrowserIDBackend(BrowserIDBackend):
def create_user(self, email):
return CustomUser.objects.create(email=email)
user = self._auth(backend=CustomUserBrowserIDBackend(),
verified_email='b@test.com')
assert isinstance(user, CustomUser)
assert user.email == 'b@test.com'
10 changes: 10 additions & 0 deletions docs/details/advanced.rst
Expand Up @@ -128,3 +128,13 @@ Signals

* **sender**: The function that created the user instance.
* **user**: The user instance that was created.


Custom User Model
-----------------

Django 1.5 allows you to specify a custom model to use in place of the built-in
User model with the ``AUTH_USER_MODEL`` setting. ``django-browserid`` supports
custom User models, however you will most likely need to subclass
``django-browserid.BrowserIDBackend`` and override the ``create_user``,
``get_user``, and ``filter_users_by_email`` functions to work with your class.

0 comments on commit a6b06b5

Please sign in to comment.