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:
try: """A django.contrib.auth backend that authenticates the user based on
user_openid = UserOpenID.objects.get( an OpenID response."""
claimed_id__exact=openid_response.identity_url)
except UserOpenID.DoesNotExist:
return None
return user_openid.user
def find_unused_username(preferred_username): def get_user(self, user_id):
"""Return an unused username, based on preferred_username."""
i = 0
while True:
username = preferred_username
if i > 0:
username += str(i)
try: try:
User.objects.get(username__exact=username) return User.objects.get(pk=user_id)
except User.DoesNotExist: except User.DoesNotExist:
return username return None
i += 1
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.
openid_response = kwargs.get('openid_response')
if openid_response is None:
return None
def fill_user_details_from_sreg(user, sreg_response): if openid_response.status != SUCCESS:
fullname = sreg_response.get('fullname') return None
if fullname:
# Do our best here ... user = None
if ' ' in fullname: try:
user.first_name, user.last_name = fullname.rsplit(None, 1) user_openid = UserOpenID.objects.get(
claimed_id__exact=openid_response.identity_url)
except UserOpenID.DoesNotExist:
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
if user is None:
email = sreg_response.get('email') return None
if email:
user.email = email
user.save()
def create_user(openid_response):
"""Create a new user from an OpenID response."""
sreg_response = sreg.SRegResponse.fromSuccessResponse(openid_response)
if sreg_response:
nickname = sreg_response.get('nickname', 'openiduser')
email = sreg_response.get('email', '')
else:
nickname = 'openiduser'
email = ''
username = find_unused_username(nickname)
user = User.objects.create_user(username, email)
if sreg_response:
fill_user_details_from_sreg(user, sreg_response)
add_openid(user, openid_response)
return user
def add_openid(user, openid_response):
existing_user = get_user(openid_response)
if existing_user is not None:
if existing_user != user:
raise IdentityAlreadyClaimed(
"The identity %s has already been claimed"
% openid_response.identity_url)
return
user_openid = UserOpenID(
user=user,
claimed_id=openid_response.identity_url,
display_id=openid_response.endpoint.getDisplayIdentifier())
user_openid.save()
def openid_authenticate(openid_response):
user = get_user(openid_response)
if user is not None:
if getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False): if getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False):
sreg_response = sreg.SRegResponse.fromSuccessResponse( sreg_response = sreg.SRegResponse.fromSuccessResponse(
openid_response) openid_response)
if sreg_response: if sreg_response:
fill_user_details_from_sreg(user, sreg_response) self.update_user_details_from_sreg(user, sreg_response)
else: return user
if getattr(settings, 'OPENID_CREATE_USERS', False):
user = create_user(openid_response) def create_user_from_openid(self, openid_response):
sreg_response = sreg.SRegResponse.fromSuccessResponse(openid_response)
# As we aren't using authenticate() here, we need to annotate the if sreg_response:
# user with the backend used. We don't currently have one though, nickname = sreg_response.get('nickname', 'openiduser')
# so fake it. email = sreg_response.get('email', '')
if user is not None: else:
user.backend = 'django.contrib.auth.backends.ModelBackend' nickname = 'openiduser'
return user email = ''
# Pick a username for the user based on their nickname,
# 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:
self.update_user_details_from_sreg(user, sreg_response)
self.associate_openid(user, openid_response)
return user
def associate_openid(self, user, openid_response):
"""Associate an OpenID with a user account."""
# Check to see if this OpenID has already been claimed.
try:
user_openid = UserOpenID.objects.get(
claimed_id__exact=openid_response.identity_url)
except UserOpenID.DoesNotExist:
user_openid = UserOpenID(
user=user,
claimed_id=openid_response.identity_url,
display_id=openid_response.endpoint.getDisplayIdentifier())
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 update_user_details_from_sreg(self, user, sreg_response):
fullname = sreg_response.get('fullname')
if fullname:
# Do our best here ...
if ' ' in fullname:
user.first_name, user.last_name = fullname.rsplit(None, 1)
else:
user.first_name = u''
user.last_name = fullname
email = sreg_response.get('email')
if email:
user.email = email
user.save()
...@@ -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