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."""
__metaclass__ = type
from django.conf import settings
from django.contrib.auth.models import User
from openid.consumer.consumer import SUCCESS
from openid.extensions import sreg
from models import UserOpenID
from django_openid_auth.models import UserOpenID
class IdentityAlreadyClaimed(Exception):
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:
user_openid = UserOpenID.objects.get(
claimed_id__exact=openid_response.identity_url)
except UserOpenID.DoesNotExist:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
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):
"""Return an unused username, based on preferred_username."""
i = 0
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
openid_response = kwargs.get('openid_response')
if openid_response is None:
return None
if openid_response.status != SUCCESS:
return None
def fill_user_details_from_sreg(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)
user = None
try:
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:
user.first_name = u''
user.last_name = fullname
user = user_openid.user
email = sreg_response.get('email')
if email:
user.email = email
user.save()
if user is None:
return None
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):
"""Create a new user from an OpenID response."""
def create_user_from_openid(self, openid_response):
sreg_response = sreg.SRegResponse.fromSuccessResponse(openid_response)
if sreg_response:
nickname = sreg_response.get('nickname', 'openiduser')
......@@ -60,47 +66,58 @@ def create_user(openid_response):
nickname = 'openiduser'
email = ''
username = find_unused_username(nickname)
user = User.objects.create_user(username, 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:
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
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
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 openid_authenticate(openid_response):
user = get_user(openid_response)
if user is not None:
if getattr(settings, 'OPENID_UPDATE_DETAILS_FROM_SREG', False):
sreg_response = sreg.SRegResponse.fromSuccessResponse(
openid_response)
if sreg_response:
fill_user_details_from_sreg(user, sreg_response)
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:
if getattr(settings, 'OPENID_CREATE_USERS', False):
user = create_user(openid_response)
user.first_name = u''
user.last_name = fullname
# As we aren't using authenticate() here, we need to annotate the
# user with the backend used. We don't currently have one though,
# so fake it.
if user is not None:
user.backend = 'django.contrib.auth.backends.ModelBackend'
return user
email = sreg_response.get('email')
if email:
user.email = email
user.save()
......@@ -2,7 +2,8 @@ import re
import urllib
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.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
......@@ -15,7 +16,6 @@ from openid.consumer.discover import DiscoveryFailure
from openid.extensions import sreg
from django_openid_auth.auth import openid_authenticate
from django_openid_auth.forms import OpenIDLoginForm
from django_openid_auth.store import DjangoOpenIDStore
......@@ -134,7 +134,7 @@ def login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME):
return HttpResponse('No OpenID response')
if openid_response.status == SUCCESS:
user = openid_authenticate(openid_response)
user = authenticate(openid_response=openid_response)
if user is not None:
if user.is_active:
auth_login(request, user)
......
......@@ -78,6 +78,11 @@ INSTALLED_APPS = (
'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?
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