Commit c17ddb15 by Anthony Lenton

Merged in lp:~mhall119/django-openid-auth/provides-784239

parents 5ad6e9b6 be751247
...@@ -154,3 +154,15 @@ If your users should use a physical multi-factor authentication method, such as ...@@ -154,3 +154,15 @@ If your users should use a physical multi-factor authentication method, such as
If the user's OpenID provider supports the PAPE extension and provides the Physical Multifactor authentication policy, this will If the user's OpenID provider supports the PAPE extension and provides the Physical Multifactor authentication policy, this will
cause the OpenID login to fail if the user does not provide valid physical authentication to the provider. cause the OpenID login to fail if the user does not provide valid physical authentication to the provider.
== Override Login Failure Handling ==
You can optionally provide your own handler for login failures by adding the following setting:
OPENID_RENDER_FAILURE = failure_handler_function
Where failure_handler_function is a function reference that will take the following parameters:
def failure_handler_function(request, message, status=None, template_name=None, exception=None)
This function must return a Django.http.HttpResponse instance.
...@@ -37,16 +37,13 @@ from openid.extensions import ax, sreg, pape ...@@ -37,16 +37,13 @@ from openid.extensions import ax, sreg, pape
from django_openid_auth import teams from django_openid_auth import teams
from django_openid_auth.models import UserOpenID from django_openid_auth.models import UserOpenID
from django_openid_auth.exceptions import (
IdentityAlreadyClaimed,
class IdentityAlreadyClaimed(Exception): DuplicateUsernameViolation,
pass MissingUsernameViolation,
MissingPhysicalMultiFactor,
class StrictUsernameViolation(Exception): RequiredAttributeNotReturned,
pass )
class RequiredAttributeNotReturned(Exception):
pass
class OpenIDBackend: class OpenIDBackend:
"""A django.contrib.auth backend that authenticates the user based on """A django.contrib.auth backend that authenticates the user based on
...@@ -92,7 +89,7 @@ class OpenIDBackend: ...@@ -92,7 +89,7 @@ class OpenIDBackend:
pape_response = pape.Response.fromSuccessResponse(openid_response) pape_response = pape.Response.fromSuccessResponse(openid_response)
if pape_response is None or \ if pape_response is None or \
pape.AUTH_MULTI_FACTOR_PHYSICAL not in pape_response.auth_policies: pape.AUTH_MULTI_FACTOR_PHYSICAL not in pape_response.auth_policies:
return None raise MissingPhysicalMultiFactor()
teams_response = teams.TeamsResponse.fromSuccessResponse( teams_response = teams.TeamsResponse.fromSuccessResponse(
openid_response) openid_response)
...@@ -150,6 +147,12 @@ class OpenIDBackend: ...@@ -150,6 +147,12 @@ class OpenIDBackend:
first_name=first_name, last_name=last_name) first_name=first_name, last_name=last_name)
def _get_available_username(self, nickname, identity_url): def _get_available_username(self, nickname, identity_url):
# If we're being strict about usernames, throw an error if we didn't
# get one back from the provider
if getattr(settings, 'OPENID_STRICT_USERNAMES', False):
if nickname is None or nickname == '':
raise MissingUsernameViolation()
# If we don't have a nickname, and we're not being strict, use a default # If we don't have a nickname, and we're not being strict, use a default
nickname = nickname or 'openiduser' nickname = nickname or 'openiduser'
...@@ -184,7 +187,7 @@ class OpenIDBackend: ...@@ -184,7 +187,7 @@ class OpenIDBackend:
if getattr(settings, 'OPENID_STRICT_USERNAMES', False): if getattr(settings, 'OPENID_STRICT_USERNAMES', False):
if User.objects.filter(username__exact=nickname).count() > 0: if User.objects.filter(username__exact=nickname).count() > 0:
raise StrictUsernameViolation( raise DuplicateUsernameViolation(
"The username (%s) with which you tried to log in is " "The username (%s) with which you tried to log in is "
"already in use for a different account." % nickname) "already in use for a different account." % nickname)
......
# django-openid-auth - OpenID integration for django.contrib.auth
#
# Copyright (C) 2008-2010 Canonical Ltd.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""Exception classes thrown by OpenID Authentication and Validation."""
class DjangoOpenIDException(Exception):
pass
class RequiredAttributeNotReturned(DjangoOpenIDException):
pass
class IdentityAlreadyClaimed(DjangoOpenIDException):
def __init__(self, message=None):
if message is None:
self.message = "Another user already exists for your selected OpenID"
else:
self.message = message
class DuplicateUsernameViolation(DjangoOpenIDException):
def __init__(self, message=None):
if message is None:
self.message = "Your desired username is already being used."
else:
self.message = message
class MissingUsernameViolation(DjangoOpenIDException):
def __init__(self, message=None):
if message is None:
self.message = "No nickname given for your account."
else:
self.message = message
class MissingPhysicalMultiFactor(DjangoOpenIDException):
def __init__(self, message=None):
if message is None:
self.message = "Login requires physical multi-factor authentication."
else:
self.message = message
...@@ -51,14 +51,14 @@ from openid.consumer.discover import DiscoveryFailure ...@@ -51,14 +51,14 @@ from openid.consumer.discover import DiscoveryFailure
from openid.extensions import sreg, ax, pape from openid.extensions import sreg, ax, pape
from django_openid_auth import teams from django_openid_auth import teams
from django_openid_auth.auth import (
RequiredAttributeNotReturned,
StrictUsernameViolation,
)
from django_openid_auth.forms import OpenIDLoginForm from django_openid_auth.forms import OpenIDLoginForm
from django_openid_auth.models import UserOpenID from django_openid_auth.models import UserOpenID
from django_openid_auth.signals import openid_login_complete from django_openid_auth.signals import openid_login_complete
from django_openid_auth.store import DjangoOpenIDStore from django_openid_auth.store import DjangoOpenIDStore
from django_openid_auth.exceptions import (
RequiredAttributeNotReturned,
DjangoOpenIDException,
)
next_url_re = re.compile('^/[-\w/]+$') next_url_re = re.compile('^/[-\w/]+$')
...@@ -122,10 +122,11 @@ def render_openid_request(request, openid_request, return_to, trust_root=None): ...@@ -122,10 +122,11 @@ def render_openid_request(request, openid_request, return_to, trust_root=None):
def default_render_failure(request, message, status=403, def default_render_failure(request, message, status=403,
template_name='openid/failure.html'): template_name='openid/failure.html',
exception=None):
"""Render an error page to the user.""" """Render an error page to the user."""
data = render_to_string( data = render_to_string(
template_name, dict(message=message), template_name, dict(message=message, exception=exception),
context_instance=RequestContext(request)) context_instance=RequestContext(request))
return HttpResponse(data, status=status) return HttpResponse(data, status=status)
...@@ -175,7 +176,8 @@ def login_begin(request, template_name='openid/login.html', ...@@ -175,7 +176,8 @@ def login_begin(request, template_name='openid/login.html',
openid_request = consumer.begin(openid_url) openid_request = consumer.begin(openid_url)
except DiscoveryFailure, exc: except DiscoveryFailure, exc:
return render_failure( return render_failure(
request, "OpenID discovery error: %s" % (str(exc),), status=500) request, "OpenID discovery error: %s" % (str(exc),), status=500,
exception=exc)
# Request some user details. If the provider advertises support # Request some user details. If the provider advertises support
# for attribute exchange, use that. # for attribute exchange, use that.
...@@ -220,7 +222,6 @@ def login_begin(request, template_name='openid/login.html', ...@@ -220,7 +222,6 @@ def login_begin(request, template_name='openid/login.html',
pape_request = pape.Request(preferred_auth_policies=preferred_auth) pape_request = pape.Request(preferred_auth_policies=preferred_auth)
openid_request.addExtension(pape_request) openid_request.addExtension(pape_request)
# Request team info # Request team info
teams_mapping_auto = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO', False) teams_mapping_auto = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO', False)
teams_mapping_auto_blacklist = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO_BLACKLIST', []) teams_mapping_auto_blacklist = getattr(settings, 'OPENID_LAUNCHPAD_TEAMS_MAPPING_AUTO_BLACKLIST', [])
...@@ -250,8 +251,11 @@ def login_begin(request, template_name='openid/login.html', ...@@ -250,8 +251,11 @@ def login_begin(request, template_name='openid/login.html',
@csrf_exempt @csrf_exempt
def login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME, def login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME,
render_failure=default_render_failure): render_failure=None):
redirect_to = request.REQUEST.get(redirect_field_name, '') redirect_to = request.REQUEST.get(redirect_field_name, '')
render_failure = render_failure or \
getattr(settings, 'OPENID_RENDER_FAILURE', None) or \
default_render_failure
openid_response = parse_openid_response(request) openid_response = parse_openid_response(request)
if not openid_response: if not openid_response:
...@@ -261,8 +265,8 @@ def login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME, ...@@ -261,8 +265,8 @@ def login_complete(request, redirect_field_name=REDIRECT_FIELD_NAME,
if openid_response.status == SUCCESS: if openid_response.status == SUCCESS:
try: try:
user = authenticate(openid_response=openid_response) user = authenticate(openid_response=openid_response)
except (StrictUsernameViolation, RequiredAttributeNotReturned), e: except DjangoOpenIDException, e:
return render_failure(request, e) return render_failure(request, e.message, exception=e)
if user is not None: if user is not None:
if user.is_active: if user.is_active:
......
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