Commit d19078e8 by James Henstridge

Convert the auth.py code into a django.contrib.auth authentication

backend.  This removes the need for the hack setting user.backend 
manually and lets dependent code use the standard authenticate() method.
parent 6f5d1282
"""Glue between OpenID and django.contrib.auth.""" """Glue between OpenID and django.contrib.auth."""
__metaclass__ = type
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from openid.consumer.consumer import SUCCESS
from openid.extensions import sreg from openid.extensions import sreg
from models import UserOpenID from django_openid_auth.models import UserOpenID
class IdentityAlreadyClaimed(Exception): class IdentityAlreadyClaimed(Exception):
pass pass
def get_user(openid_response): class OpenIDBackend:
"""A django.contrib.auth backend that authenticates the user based on
an OpenID response."""
def get_user(self, user_id):
try: try:
user_openid = UserOpenID.objects.get( return User.objects.get(pk=user_id)
claimed_id__exact=openid_response.identity_url) except User.DoesNotExist:
except UserOpenID.DoesNotExist:
return None return None
return user_openid.user
def authenticate(self, **kwargs):
"""Authenticate the user based on an OpenID response."""
# Require that the OpenID response be passed in as a keyword
# argument, to make sure we don't match the username/password
# calling conventions of authenticate.
def find_unused_username(preferred_username): openid_response = kwargs.get('openid_response')
"""Return an unused username, based on preferred_username.""" if openid_response is None:
i = 0 return None
while True:
username = preferred_username
if i > 0:
username += str(i)
try:
User.objects.get(username__exact=username)
except User.DoesNotExist:
return username
i += 1
if openid_response.status != SUCCESS:
return None
def fill_user_details_from_sreg(user, sreg_response): user = None
fullname = sreg_response.get('fullname') try:
if fullname: user_openid = UserOpenID.objects.get(
# Do our best here ... claimed_id__exact=openid_response.identity_url)
if ' ' in fullname: except UserOpenID.DoesNotExist:
user.first_name, user.last_name = fullname.rsplit(None, 1) if getattr(settings, 'OPENID_CREATE_USERS', False):
user = self.create_user_from_openid(openid_response)
else: else:
user.first_name = u'' user = user_openid.user
user.last_name = fullname
email = sreg_response.get('email') if user is None:
if email: return None
user.email = email
user.save()
if getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False):
sreg_response = sreg.SRegResponse.fromSuccessResponse(
openid_response)
if sreg_response:
self.update_user_details_from_sreg(user, sreg_response)
return user
def create_user(openid_response): def create_user_from_openid(self, openid_response):
"""Create a new user from an OpenID response."""
sreg_response = sreg.SRegResponse.fromSuccessResponse(openid_response) sreg_response = sreg.SRegResponse.fromSuccessResponse(openid_response)
if sreg_response: if sreg_response:
nickname = sreg_response.get('nickname', 'openiduser') nickname = sreg_response.get('nickname', 'openiduser')
...@@ -60,47 +66,58 @@ def create_user(openid_response): ...@@ -60,47 +66,58 @@ def create_user(openid_response):
nickname = 'openiduser' nickname = 'openiduser'
email = '' email = ''
username = find_unused_username(nickname) # Pick a username for the user based on their nickname,
user = User.objects.create_user(username, email) # checking for conflicts.
i = 1
while True:
username = nickname
if i > 1:
username += str(i)
try:
User.objects.get(username__exact=username)
except User.DoesNotExist:
break
i += 1
user = User.objects.create_user(username, email, password=None)
if sreg_response: if sreg_response:
fill_user_details_from_sreg(user, sreg_response) self.update_user_details_from_sreg(user, sreg_response)
add_openid(user, openid_response) self.associate_openid(user, openid_response)
return user return user
def associate_openid(self, user, openid_response):
def add_openid(user, openid_response): """Associate an OpenID with a user account."""
existing_user = get_user(openid_response) # Check to see if this OpenID has already been claimed.
if existing_user is not None: try:
if existing_user != user: user_openid = UserOpenID.objects.get(
raise IdentityAlreadyClaimed( claimed_id__exact=openid_response.identity_url)
"The identity %s has already been claimed" except UserOpenID.DoesNotExist:
% openid_response.identity_url)
return
user_openid = UserOpenID( user_openid = UserOpenID(
user=user, user=user,
claimed_id=openid_response.identity_url, claimed_id=openid_response.identity_url,
display_id=openid_response.endpoint.getDisplayIdentifier()) display_id=openid_response.endpoint.getDisplayIdentifier())
user_openid.save() user_openid.save()
else:
if user_openid.user != user:
raise IdentityAlreadyClaimed(
"The identity %s has already been claimed"
% openid_response.identity_url)
return user_openid
def openid_authenticate(openid_response): def update_user_details_from_sreg(self, user, sreg_response):
user = get_user(openid_response) fullname = sreg_response.get('fullname')
if user is not None: if fullname:
if getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False): # Do our best here ...
sreg_response = sreg.SRegResponse.fromSuccessResponse( if ' ' in fullname:
openid_response) user.first_name, user.last_name = fullname.rsplit(None, 1)
if sreg_response:
fill_user_details_from_sreg(user, sreg_response)
else: else:
if getattr(settings, 'OPENID_CREATE_USERS', False): user.first_name = u''
user = create_user(openid_response) user.last_name = fullname
# As we aren't using authenticate() here, we need to annotate the email = sreg_response.get('email')
# user with the backend used. We don't currently have one though, if email:
# so fake it. user.email = email
if user is not None: user.save()
user.backend = 'django.contrib.auth.backends.ModelBackend'
return user
...@@ -2,7 +2,8 @@ import re ...@@ -2,7 +2,8 @@ import re
import urllib import urllib
from django.conf import settings from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login from django.contrib.auth import (
REDIRECT_FIELD_NAME, authenticate, login as auth_login)
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
...@@ -15,7 +16,6 @@ from openid.consumer.discover import DiscoveryFailure ...@@ -15,7 +16,6 @@ from openid.consumer.discover import DiscoveryFailure
from openid.extensions import sreg from openid.extensions import sreg
from django_openid_auth.auth import openid_authenticate
from django_openid_auth.forms import OpenIDLoginForm from django_openid_auth.forms import OpenIDLoginForm
from django_openid_auth.store import DjangoOpenIDStore from django_openid_auth.store import DjangoOpenIDStore
...@@ -134,7 +134,7 @@ def login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME): ...@@ -134,7 +134,7 @@ def login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME):
return HttpResponse('No OpenID response') return HttpResponse('No OpenID response')
if openid_response.status == SUCCESS: if openid_response.status == SUCCESS:
user = openid_authenticate(openid_response) user = authenticate(openid_response=openid_response)
if user is not None: if user is not None:
if user.is_active: if user.is_active:
auth_login(request, user) auth_login(request, user)
......
...@@ -78,6 +78,11 @@ INSTALLED_APPS = ( ...@@ -78,6 +78,11 @@ INSTALLED_APPS = (
'django_openid_auth', 'django_openid_auth',
) )
AUTHENTICATION_BACKENDS = (
'django_openid_auth.auth.OpenIDBackend',
'django.contrib.auth.backends.ModelBackend',
)
# Should users be created when new OpenIDs are used to log in? # Should users be created when new OpenIDs are used to log in?
OPENID_CREATE_USERS = True OPENID_CREATE_USERS = True
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment