Commit fd49f88e by Nimisha Asthagiri

Moves external_auth from common to openedx/core.

parent ce1eb237
...@@ -10,8 +10,11 @@ from django.conf import settings ...@@ -10,8 +10,11 @@ from django.conf import settings
from edxmako.shortcuts import render_to_response from edxmako.shortcuts import render_to_response
from external_auth.views import (ssl_login_shortcut, ssl_get_cert_from_request, from openedx.core.djangoapps.external_auth.views import (
redirect_with_get) ssl_login_shortcut,
ssl_get_cert_from_request,
redirect_with_get,
)
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
__all__ = ['signup', 'login_page', 'howitworks'] __all__ = ['signup', 'login_page', 'howitworks']
......
...@@ -817,7 +817,7 @@ INSTALLED_APPS = ( ...@@ -817,7 +817,7 @@ INSTALLED_APPS = (
'contentstore', 'contentstore',
'contentserver', 'contentserver',
'course_creators', 'course_creators',
'external_auth', 'openedx.core.djangoapps.external_auth',
'student', # misleading name due to sharing with lms 'student', # misleading name due to sharing with lms
'openedx.core.djangoapps.course_groups', # not used in cms (yet), but tests run 'openedx.core.djangoapps.course_groups', # not used in cms (yet), but tests run
'openedx.core.djangoapps.coursetalk', # not used in cms (yet), but tests run 'openedx.core.djangoapps.coursetalk', # not used in cms (yet), but tests run
......
...@@ -153,7 +153,7 @@ if settings.FEATURES.get('ENABLE_SERVICE_STATUS'): ...@@ -153,7 +153,7 @@ if settings.FEATURES.get('ENABLE_SERVICE_STATUS'):
if settings.FEATURES.get('AUTH_USE_CAS'): if settings.FEATURES.get('AUTH_USE_CAS'):
urlpatterns += ( urlpatterns += (
url(r'^cas-auth/login/$', 'external_auth.views.cas_login', name="cas-login"), url(r'^cas-auth/login/$', 'openedx.core.djangoapps.external_auth.views.cas_login', name="cas-login"),
url(r'^cas-auth/logout/$', 'django_cas.views.logout', {'next_page': '/'}, name="cas-logout"), url(r'^cas-auth/logout/$', 'django_cas.views.logout', {'next_page': '/'}, name="cas-logout"),
) )
......
...@@ -16,7 +16,7 @@ import mock ...@@ -16,7 +16,7 @@ import mock
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
from lang_pref import LANGUAGE_KEY from lang_pref import LANGUAGE_KEY
from notification_prefs import NOTIFICATION_PREF_KEY from notification_prefs import NOTIFICATION_PREF_KEY
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
import student import student
from student.models import UserAttribute from student.models import UserAttribute
from student.views import REGISTRATION_AFFILIATE_ID from student.views import REGISTRATION_AFFILIATE_ID
......
...@@ -16,7 +16,7 @@ import httpretty ...@@ -16,7 +16,7 @@ import httpretty
from mock import patch from mock import patch
from social.apps.django_app.default.models import UserSocialAuth from social.apps.django_app.default.models import UserSocialAuth
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from student.tests.factories import UserFactory, RegistrationFactory, UserProfileFactory from student.tests.factories import UserFactory, RegistrationFactory, UserProfileFactory
from student.views import login_oauth_token from student.views import login_oauth_token
......
...@@ -11,7 +11,7 @@ from importlib import import_module ...@@ -11,7 +11,7 @@ from importlib import import_module
from django.test.utils import override_settings from django.test.utils import override_settings
from django.conf import settings from django.conf import settings
from mock import patch from mock import patch
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
from student.views import create_account from student.views import create_account
......
...@@ -78,9 +78,9 @@ from courseware.access import has_access ...@@ -78,9 +78,9 @@ from courseware.access import has_access
from django_comment_common.models import Role from django_comment_common.models import Role
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
import external_auth.views import openedx.core.djangoapps.external_auth.views
from external_auth.login_and_register import ( from openedx.core.djangoapps.external_auth.login_and_register import (
login as external_auth_login, login as external_auth_login,
register as external_auth_register register as external_auth_register
) )
...@@ -470,7 +470,9 @@ def register_user(request, extra_context=None): ...@@ -470,7 +470,9 @@ def register_user(request, extra_context=None):
if extra_context is not None: if extra_context is not None:
context.update(extra_context) context.update(extra_context)
if context.get("extauth_domain", '').startswith(external_auth.views.SHIBBOLETH_DOMAIN_PREFIX): if context.get("extauth_domain", '').startswith(
openedx.core.djangoapps.external_auth.views.SHIBBOLETH_DOMAIN_PREFIX
):
return render_to_response('register-shib.html', context) return render_to_response('register-shib.html', context)
# If third-party auth is enabled, prepopulate the form with data from the # If third-party auth is enabled, prepopulate the form with data from the
...@@ -1195,7 +1197,7 @@ def login_user(request, error=""): # pylint: disable=too-many-statements,unused ...@@ -1195,7 +1197,7 @@ def login_user(request, error=""): # pylint: disable=too-many-statements,unused
if settings.FEATURES.get('AUTH_USE_SHIB') and user: if settings.FEATURES.get('AUTH_USE_SHIB') and user:
try: try:
eamap = ExternalAuthMap.objects.get(user=user) eamap = ExternalAuthMap.objects.get(user=user)
if eamap.external_domain.startswith(external_auth.views.SHIBBOLETH_DOMAIN_PREFIX): if eamap.external_domain.startswith(openedx.core.djangoapps.external_auth.views.SHIBBOLETH_DOMAIN_PREFIX):
return JsonResponse({ return JsonResponse({
"success": False, "success": False,
"redirect": reverse('shib-login'), "redirect": reverse('shib-login'),
...@@ -1637,9 +1639,7 @@ def create_account_with_params(request, params): ...@@ -1637,9 +1639,7 @@ def create_account_with_params(request, params):
not settings.FEATURES.get("AUTH_USE_SHIB") or not settings.FEATURES.get("AUTH_USE_SHIB") or
not settings.FEATURES.get("SHIB_DISABLE_TOS") or not settings.FEATURES.get("SHIB_DISABLE_TOS") or
not do_external_auth or not do_external_auth or
not eamap.external_domain.startswith( not eamap.external_domain.startswith(openedx.core.djangoapps.external_auth.views.SHIBBOLETH_DOMAIN_PREFIX)
external_auth.views.SHIBBOLETH_DOMAIN_PREFIX
)
) )
form = AccountCreationForm( form = AccountCreationForm(
......
...@@ -101,7 +101,7 @@ MOCK_MODULES = [ ...@@ -101,7 +101,7 @@ MOCK_MODULES = [
'openid', 'openid',
'openid.store', 'openid.store',
'openid.store.interface', 'openid.store.interface',
'external_auth.views', 'openedx.core.djangoapps.external_auth.views',
'mail_utils', 'mail_utils',
'ratelimitbackend.backends', 'ratelimitbackend.backends',
'social.apps.django_app.default', 'social.apps.django_app.default',
......
...@@ -61,7 +61,7 @@ def index(request): ...@@ -61,7 +61,7 @@ def index(request):
return redirect(reverse('dashboard')) return redirect(reverse('dashboard'))
if settings.FEATURES.get('AUTH_USE_CERTIFICATES'): if settings.FEATURES.get('AUTH_USE_CERTIFICATES'):
from external_auth.views import ssl_login from openedx.core.djangoapps.external_auth.views import ssl_login
# Set next URL to dashboard if it isn't set to avoid # Set next URL to dashboard if it isn't set to avoid
# caching a redirect to / that causes a redirect loop on logout # caching a redirect to / that causes a redirect loop on logout
if not request.GET.get('next'): if not request.GET.get('next'):
......
...@@ -33,7 +33,7 @@ from xmodule.x_module import XModule ...@@ -33,7 +33,7 @@ from xmodule.x_module import XModule
from xmodule.split_test_module import get_split_user_partitions from xmodule.split_test_module import get_split_user_partitions
from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPartitionGroupError from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPartitionGroupError
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
from courseware.masquerade import get_masquerade_role, is_masquerading_as_student from courseware.masquerade import get_masquerade_role, is_masquerading_as_student
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from student import auth from student import auth
......
...@@ -34,8 +34,8 @@ import dashboard.git_import as git_import ...@@ -34,8 +34,8 @@ import dashboard.git_import as git_import
from dashboard.git_import import GitImportError from dashboard.git_import import GitImportError
from student.roles import CourseStaffRole, CourseInstructorRole from student.roles import CourseStaffRole, CourseInstructorRole
from dashboard.models import CourseImportLog from dashboard.models import CourseImportLog
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
from external_auth.views import generate_password from openedx.core.djangoapps.external_auth.views import generate_password
from student.models import CourseEnrollment, UserProfile, Registration from student.models import CourseEnrollment, UserProfile, Registration
import track.views import track.views
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
......
...@@ -15,7 +15,7 @@ import readline ...@@ -15,7 +15,7 @@ import readline
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from student.models import UserProfile, Registration from student.models import UserProfile, Registration
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from pytz import UTC from pytz import UTC
......
...@@ -22,7 +22,7 @@ from edxmako.shortcuts import render_to_response ...@@ -22,7 +22,7 @@ from edxmako.shortcuts import render_to_response
import pytz import pytz
from commerce.models import CommerceConfiguration from commerce.models import CommerceConfiguration
from external_auth.login_and_register import ( from openedx.core.djangoapps.external_auth.login_and_register import (
login as external_auth_login, login as external_auth_login,
register as external_auth_register register as external_auth_register
) )
......
...@@ -1917,7 +1917,7 @@ INSTALLED_APPS = ( ...@@ -1917,7 +1917,7 @@ INSTALLED_APPS = (
'support', 'support',
# External auth (OpenID, shib) # External auth (OpenID, shib)
'external_auth', 'openedx.core.djangoapps.external_auth',
'django_openid_auth', 'django_openid_auth',
# django-oauth2-provider (deprecated) # django-oauth2-provider (deprecated)
......
...@@ -803,27 +803,31 @@ if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'): ...@@ -803,27 +803,31 @@ if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'):
if settings.FEATURES.get('AUTH_USE_OPENID'): if settings.FEATURES.get('AUTH_USE_OPENID'):
urlpatterns += ( urlpatterns += (
url(r'^openid/login/$', 'django_openid_auth.views.login_begin', name='openid-login'), url(r'^openid/login/$', 'django_openid_auth.views.login_begin', name='openid-login'),
url(r'^openid/complete/$', 'external_auth.views.openid_login_complete', name='openid-complete'), url(
r'^openid/complete/$',
'openedx.core.djangoapps.external_auth.views.openid_login_complete',
name='openid-complete',
),
url(r'^openid/logo.gif$', 'django_openid_auth.views.logo', name='openid-logo'), url(r'^openid/logo.gif$', 'django_openid_auth.views.logo', name='openid-logo'),
) )
if settings.FEATURES.get('AUTH_USE_SHIB'): if settings.FEATURES.get('AUTH_USE_SHIB'):
urlpatterns += ( urlpatterns += (
url(r'^shib-login/$', 'external_auth.views.shib_login', name='shib-login'), url(r'^shib-login/$', 'openedx.core.djangoapps.external_auth.views.shib_login', name='shib-login'),
) )
if settings.FEATURES.get('AUTH_USE_CAS'): if settings.FEATURES.get('AUTH_USE_CAS'):
urlpatterns += ( urlpatterns += (
url(r'^cas-auth/login/$', 'external_auth.views.cas_login', name="cas-login"), url(r'^cas-auth/login/$', 'openedx.core.djangoapps.external_auth.views.cas_login', name="cas-login"),
url(r'^cas-auth/logout/$', 'django_cas.views.logout', {'next_page': '/'}, name="cas-logout"), url(r'^cas-auth/logout/$', 'django_cas.views.logout', {'next_page': '/'}, name="cas-logout"),
) )
if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD'): if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD'):
urlpatterns += ( urlpatterns += (
url(r'^course_specific_login/{}/$'.format(settings.COURSE_ID_PATTERN), url(r'^course_specific_login/{}/$'.format(settings.COURSE_ID_PATTERN),
'external_auth.views.course_specific_login', name='course-specific-login'), 'openedx.core.djangoapps.external_auth.views.course_specific_login', name='course-specific-login'),
url(r'^course_specific_register/{}/$'.format(settings.COURSE_ID_PATTERN), url(r'^course_specific_register/{}/$'.format(settings.COURSE_ID_PATTERN),
'external_auth.views.course_specific_register', name='course-specific-register'), 'openedx.core.djangoapps.external_auth.views.course_specific_register', name='course-specific-register'),
) )
...@@ -846,14 +850,26 @@ urlpatterns += ( ...@@ -846,14 +850,26 @@ urlpatterns += (
if settings.FEATURES.get('AUTH_USE_OPENID_PROVIDER'): if settings.FEATURES.get('AUTH_USE_OPENID_PROVIDER'):
urlpatterns += ( urlpatterns += (
url(r'^openid/provider/login/$', 'external_auth.views.provider_login', name='openid-provider-login'), url(
r'^openid/provider/login/$',
'openedx.core.djangoapps.external_auth.views.provider_login',
name='openid-provider-login',
),
url( url(
r'^openid/provider/login/(?:.+)$', r'^openid/provider/login/(?:.+)$',
'external_auth.views.provider_identity', 'openedx.core.djangoapps.external_auth.views.provider_identity',
name='openid-provider-login-identity' name='openid-provider-login-identity'
), ),
url(r'^openid/provider/identity/$', 'external_auth.views.provider_identity', name='openid-provider-identity'), url(
url(r'^openid/provider/xrds/$', 'external_auth.views.provider_xrds', name='openid-provider-xrds') r'^openid/provider/identity/$',
'openedx.core.djangoapps.external_auth.views.provider_identity',
name='openid-provider-identity',
),
url(
r'^openid/provider/xrds/$',
'openedx.core.djangoapps.external_auth.views.provider_xrds',
name='openid-provider-xrds',
),
) )
if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'): if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
......
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
django admin pages for courseware model django admin pages for courseware model
''' '''
from external_auth.models import * from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
from ratelimitbackend import admin from ratelimitbackend import admin
class ExternalAuthMapAdmin(admin.ModelAdmin): class ExternalAuthMapAdmin(admin.ModelAdmin):
"""
Admin model for ExternalAuthMap
"""
search_fields = ['external_id', 'user__username'] search_fields = ['external_id', 'user__username']
date_hierarchy = 'dtcreated' date_hierarchy = 'dtcreated'
......
...@@ -18,19 +18,28 @@ log = logging.getLogger('DjangoOpenIDStore') ...@@ -18,19 +18,28 @@ log = logging.getLogger('DjangoOpenIDStore')
def get_url_key(server_url): def get_url_key(server_url):
key = ASSOCIATIONS_KEY_PREFIX + server_url """
return key Returns the URL key for the given server_url.
"""
return ASSOCIATIONS_KEY_PREFIX + server_url
def get_nonce_key(server_url, timestamp, salt): def get_nonce_key(server_url, timestamp, salt):
key = '{prefix}{url}.{ts}.{salt}'.format(prefix=NONCE_KEY_PREFIX, """
Returns the nonce for the given parameters.
"""
return '{prefix}{url}.{ts}.{salt}'.format(
prefix=NONCE_KEY_PREFIX,
url=server_url, url=server_url,
ts=timestamp, ts=timestamp,
salt=salt) salt=salt,
return key )
class DjangoOpenIDStore(OpenIDStore): class DjangoOpenIDStore(OpenIDStore):
"""
django implementation of OpenIDStore.
"""
def __init__(self): def __init__(self):
log.info('DjangoStore cache:' + str(cache.__class__)) log.info('DjangoStore cache:' + str(cache.__class__))
......
...@@ -7,7 +7,7 @@ import re ...@@ -7,7 +7,7 @@ import re
from django.conf import settings from django.conf import settings
from django.shortcuts import redirect from django.shortcuts import redirect
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
import external_auth.views import openedx.core.djangoapps.external_auth.views
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
...@@ -56,11 +56,14 @@ def login(request): ...@@ -56,11 +56,14 @@ def login(request):
# is not handling the request. # is not handling the request.
response = None response = None
if settings.FEATURES['AUTH_USE_CERTIFICATES'] and external_auth.views.ssl_get_cert_from_request(request): if (
settings.FEATURES['AUTH_USE_CERTIFICATES'] and
openedx.core.djangoapps.external_auth.views.ssl_get_cert_from_request(request)
):
# SSL login doesn't require a view, so redirect # SSL login doesn't require a view, so redirect
# branding and allow that to process the login if it # branding and allow that to process the login if it
# is enabled and the header is in the request. # is enabled and the header is in the request.
response = external_auth.views.redirect_with_get('root', request.GET) response = openedx.core.djangoapps.external_auth.views.redirect_with_get('root', request.GET)
elif settings.FEATURES.get('AUTH_USE_CAS'): elif settings.FEATURES.get('AUTH_USE_CAS'):
# If CAS is enabled, redirect auth handling to there # If CAS is enabled, redirect auth handling to there
response = redirect(reverse('cas-login')) response = redirect(reverse('cas-login'))
...@@ -69,7 +72,10 @@ def login(request): ...@@ -69,7 +72,10 @@ def login(request):
if redirect_to: if redirect_to:
course_id = _parse_course_id_from_string(redirect_to) course_id = _parse_course_id_from_string(redirect_to)
if course_id and _get_course_enrollment_domain(course_id): if course_id and _get_course_enrollment_domain(course_id):
response = external_auth.views.course_specific_login(request, course_id.to_deprecated_string()) response = openedx.core.djangoapps.external_auth.views.course_specific_login(
request,
course_id.to_deprecated_string(),
)
return response return response
...@@ -88,5 +94,5 @@ def register(request): ...@@ -88,5 +94,5 @@ def register(request):
if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'): if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'):
# Redirect to branding to process their certificate if SSL is enabled # Redirect to branding to process their certificate if SSL is enabled
# and registration is disabled. # and registration is disabled.
response = external_auth.views.redirect_with_get('root', request.GET) response = openedx.core.djangoapps.external_auth.views.redirect_with_get('root', request.GET)
return response return response
...@@ -6,7 +6,7 @@ file and check it in at the same time as your model changes. To do that, ...@@ -6,7 +6,7 @@ file and check it in at the same time as your model changes. To do that,
1. Go to the edx-platform dir 1. Go to the edx-platform dir
2. ./manage.py lms schemamigration student --auto description_of_your_change 2. ./manage.py lms schemamigration student --auto description_of_your_change
3. Add the migration file created in edx-platform/common/djangoapps/external_auth/migrations/ 3. Add the migration file created in edx-platform/openedx/core/djangoapps/external_auth/migrations/
""" """
from django.db import models from django.db import models
...@@ -14,6 +14,9 @@ from django.contrib.auth.models import User ...@@ -14,6 +14,9 @@ from django.contrib.auth.models import User
class ExternalAuthMap(models.Model): class ExternalAuthMap(models.Model):
"""
Model class for external auth.
"""
class Meta(object): class Meta(object):
app_label = "external_auth" app_label = "external_auth"
unique_together = (('external_id', 'external_domain'), ) unique_together = (('external_id', 'external_domain'), )
...@@ -29,5 +32,4 @@ class ExternalAuthMap(models.Model): ...@@ -29,5 +32,4 @@ class ExternalAuthMap(models.Model):
dtsignup = models.DateTimeField('signup date', null=True) # set after signup dtsignup = models.DateTimeField('signup date', null=True) # set after signup
def __unicode__(self): def __unicode__(self):
s = "[%s] = (%s / %s)" % (self.external_id, self.external_name, self.external_email) return "[%s] = (%s / %s)" % (self.external_id, self.external_name, self.external_email)
return s
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Tests for utility functions in external_auth module Tests for utility functions in external_auth module
""" """
from django.test import TestCase from django.test import TestCase
from external_auth.views import _safe_postlogin_redirect from openedx.core.djangoapps.external_auth.views import _safe_postlogin_redirect
class ExternalAuthHelperFnTest(TestCase): class ExternalAuthHelperFnTest(TestCase):
......
...@@ -17,7 +17,7 @@ from django.test.client import RequestFactory ...@@ -17,7 +17,7 @@ from django.test.client import RequestFactory
from unittest import skipUnless from unittest import skipUnless
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from external_auth.views import provider_login from openedx.core.djangoapps.external_auth.views import provider_login
class MyFetcher(HTTPFetcher): class MyFetcher(HTTPFetcher):
...@@ -130,27 +130,53 @@ class OpenIdProviderTest(TestCase): ...@@ -130,27 +130,53 @@ class OpenIdProviderTest(TestCase):
self.assertEqual(resp.status_code, code, self.assertEqual(resp.status_code, code,
"got code {0} for url '{1}'. Expected code {2}" "got code {0} for url '{1}'. Expected code {2}"
.format(resp.status_code, url, code)) .format(resp.status_code, url, code))
self.assertContains(resp, '<input name="openid.mode" type="hidden" value="checkid_setup" />', html=True) for expected_input in (
self.assertContains(resp, '<input name="openid.ns" type="hidden" value="http://specs.openid.net/auth/2.0" />', html=True) '<input name="openid.ns" type="hidden" value="http://specs.openid.net/auth/2.0" />',
self.assertContains(resp, '<input name="openid.identity" type="hidden" value="http://specs.openid.net/auth/2.0/identifier_select" />', html=True)
self.assertContains(resp, '<input name="openid.claimed_id" type="hidden" value="http://specs.openid.net/auth/2.0/identifier_select" />', html=True) '<input name="openid.ns.ax" type="hidden" value="http://openid.net/srv/ax/1.0" />',
self.assertContains(resp, '<input name="openid.ns.ax" type="hidden" value="http://openid.net/srv/ax/1.0" />', html=True)
self.assertContains(resp, '<input name="openid.ax.mode" type="hidden" value="fetch_request" />', html=True) '<input name="openid.ax.type.fullname" type="hidden" value="http://axschema.org/namePerson" />',
self.assertContains(resp, '<input name="openid.ax.required" type="hidden" value="email,fullname,old_email,firstname,old_nickname,lastname,old_fullname,nickname" />', html=True)
self.assertContains(resp, '<input name="openid.ax.type.fullname" type="hidden" value="http://axschema.org/namePerson" />', html=True) '<input type="submit" value="Continue" />',
self.assertContains(resp, '<input name="openid.ax.type.lastname" type="hidden" value="http://axschema.org/namePerson/last" />', html=True)
self.assertContains(resp, '<input name="openid.ax.type.firstname" type="hidden" value="http://axschema.org/namePerson/first" />', html=True) '<input name="openid.ax.type.email" type="hidden" value="http://axschema.org/contact/email" />',
self.assertContains(resp, '<input name="openid.ax.type.nickname" type="hidden" value="http://axschema.org/namePerson/friendly" />', html=True)
self.assertContains(resp, '<input name="openid.ax.type.email" type="hidden" value="http://axschema.org/contact/email" />', html=True) '<input name="openid.ax.type.lastname" '
self.assertContains(resp, '<input name="openid.ax.type.old_email" type="hidden" value="http://schema.openid.net/contact/email" />', html=True) 'type="hidden" value="http://axschema.org/namePerson/last" />',
self.assertContains(resp, '<input name="openid.ax.type.old_nickname" type="hidden" value="http://schema.openid.net/namePerson/friendly" />', html=True)
self.assertContains(resp, '<input name="openid.ax.type.old_fullname" type="hidden" value="http://schema.openid.net/namePerson" />', html=True) '<input name="openid.ax.type.firstname" '
self.assertContains(resp, '<input type="submit" value="Continue" />', html=True) 'type="hidden" value="http://axschema.org/namePerson/first" />',
# this should work on the server:
self.assertContains(resp, '<input name="openid.realm" type="hidden" value="http://testserver/" />', html=True) '<input name="openid.ax.required" type="hidden" '
'value="email,fullname,old_email,firstname,old_nickname,lastname,old_fullname,nickname" />',
'<input name="openid.ax.type.nickname" '
'type="hidden" value="http://axschema.org/namePerson/friendly" />',
'<input name="openid.ax.type.old_email" '
'type="hidden" value="http://schema.openid.net/contact/email" />',
'<input name="openid.ax.type.old_nickname" '
'type="hidden" value="http://schema.openid.net/namePerson/friendly" />',
'<input name="openid.ax.type.old_fullname" '
'type="hidden" value="http://schema.openid.net/namePerson" />',
'<input name="openid.identity" '
'type="hidden" value="http://specs.openid.net/auth/2.0/identifier_select" />',
'<input name="openid.claimed_id" '
'type="hidden" value="http://specs.openid.net/auth/2.0/identifier_select" />',
# should work on the test server as well
'<input name="openid.realm" '
'type="hidden" value="http://testserver/" />',
):
self.assertContains(resp, expected_input, html=True)
# not included here are elements that will vary from run to run: # not included here are elements that will vary from run to run:
# <input name="openid.return_to" type="hidden" value="http://testserver/openid/complete/?janrain_nonce=2013-01-23T06%3A20%3A17ZaN7j6H" /> # <input name="openid.return_to" type="hidden"
# value="http://testserver/openid/complete/?janrain_nonce=2013-01-23T06%3A20%3A17ZaN7j6H" />
# <input name="openid.assoc_handle" type="hidden" value="{HMAC-SHA1}{50ff8120}{rh87+Q==}" /> # <input name="openid.assoc_handle" type="hidden" value="{HMAC-SHA1}{50ff8120}{rh87+Q==}" />
def attempt_login(self, expected_code, login_method='POST', **kwargs): def attempt_login(self, expected_code, login_method='POST', **kwargs):
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#pylint: disable=no-member
""" """
Tests for Shibboleth Authentication Tests for Shibboleth Authentication
@jbau @jbau
...@@ -14,8 +15,8 @@ from django.test.utils import override_settings ...@@ -14,8 +15,8 @@ from django.test.utils import override_settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib.auth.models import AnonymousUser, User from django.contrib.auth.models import AnonymousUser, User
from importlib import import_module from importlib import import_module
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
from external_auth.views import ( from openedx.core.djangoapps.external_auth.views import (
shib_login, course_specific_login, course_specific_register, _flatten_to_ascii shib_login, course_specific_login, course_specific_register, _flatten_to_ascii
) )
from mock import patch from mock import patch
...@@ -125,6 +126,7 @@ class ShibSPTest(CacheIsolationTestCase): ...@@ -125,6 +126,7 @@ class ShibSPTest(CacheIsolationTestCase):
of an existing user that already has an ExternalAuthMap causes an error (403) of an existing user that already has an ExternalAuthMap causes an error (403)
* shib credentials that do not match an existing ExternalAuthMap causes the registration form to appear * shib credentials that do not match an existing ExternalAuthMap causes the registration form to appear
""" """
# pylint: disable=too-many-statements
user_w_map = UserFactory.create(email='withmap@stanford.edu') user_w_map = UserFactory.create(email='withmap@stanford.edu')
extauth = ExternalAuthMap(external_id='withmap@stanford.edu', extauth = ExternalAuthMap(external_id='withmap@stanford.edu',
...@@ -155,7 +157,7 @@ class ShibSPTest(CacheIsolationTestCase): ...@@ -155,7 +157,7 @@ class ShibSPTest(CacheIsolationTestCase):
for remote_user in remote_users: for remote_user in remote_users:
self.client.logout() self.client.logout()
with patch('external_auth.views.AUDIT_LOG') as mock_audit_log: with patch('openedx.core.djangoapps.external_auth.views.AUDIT_LOG') as mock_audit_log:
response = self.client.get( response = self.client.get(
reverse('shib-login'), reverse('shib-login'),
**{ **{
...@@ -214,7 +216,7 @@ class ShibSPTest(CacheIsolationTestCase): ...@@ -214,7 +216,7 @@ class ShibSPTest(CacheIsolationTestCase):
# no audit logging calls # no audit logging calls
self.assertEquals(len(audit_log_calls), 0) self.assertEquals(len(audit_log_calls), 0)
def _base_test_extauth_auto_activate_user_with_flag(self, log_user_string="inactive@stanford.edu"): def _test_auto_activate_user_with_flag(self, log_user_string="inactive@stanford.edu"):
""" """
Tests that FEATURES['BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH'] means extauth automatically Tests that FEATURES['BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH'] means extauth automatically
linked users, activates them, and logs them in linked users, activates them, and logs them in
...@@ -231,7 +233,7 @@ class ShibSPTest(CacheIsolationTestCase): ...@@ -231,7 +233,7 @@ class ShibSPTest(CacheIsolationTestCase):
}) })
request.user = AnonymousUser() request.user = AnonymousUser()
with patch('external_auth.views.AUDIT_LOG') as mock_audit_log: with patch('openedx.core.djangoapps.external_auth.views.AUDIT_LOG') as mock_audit_log:
response = shib_login(request) response = shib_login(request)
audit_log_calls = mock_audit_log.method_calls audit_log_calls = mock_audit_log.method_calls
# reload user from db, since the view function works via db side-effects # reload user from db, since the view function works via db side-effects
...@@ -256,7 +258,7 @@ class ShibSPTest(CacheIsolationTestCase): ...@@ -256,7 +258,7 @@ class ShibSPTest(CacheIsolationTestCase):
""" """
Wrapper to run base_test_extauth_auto_activate_user_with_flag with {'SQUELCH_PII_IN_LOGS': False} Wrapper to run base_test_extauth_auto_activate_user_with_flag with {'SQUELCH_PII_IN_LOGS': False}
""" """
self._base_test_extauth_auto_activate_user_with_flag(log_user_string="inactive@stanford.edu") self._test_auto_activate_user_with_flag(log_user_string="inactive@stanford.edu")
@unittest.skipUnless(settings.FEATURES.get('AUTH_USE_SHIB'), "AUTH_USE_SHIB not set") @unittest.skipUnless(settings.FEATURES.get('AUTH_USE_SHIB'), "AUTH_USE_SHIB not set")
@patch.dict(settings.FEATURES, {'BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH': True, 'SQUELCH_PII_IN_LOGS': True}) @patch.dict(settings.FEATURES, {'BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH': True, 'SQUELCH_PII_IN_LOGS': True})
...@@ -264,7 +266,7 @@ class ShibSPTest(CacheIsolationTestCase): ...@@ -264,7 +266,7 @@ class ShibSPTest(CacheIsolationTestCase):
""" """
Wrapper to run base_test_extauth_auto_activate_user_with_flag with {'SQUELCH_PII_IN_LOGS': True} Wrapper to run base_test_extauth_auto_activate_user_with_flag with {'SQUELCH_PII_IN_LOGS': True}
""" """
self._base_test_extauth_auto_activate_user_with_flag(log_user_string="user.id: 1") self._test_auto_activate_user_with_flag(log_user_string="user.id: 1")
@unittest.skipUnless(settings.FEATURES.get('AUTH_USE_SHIB'), "AUTH_USE_SHIB not set") @unittest.skipUnless(settings.FEATURES.get('AUTH_USE_SHIB'), "AUTH_USE_SHIB not set")
@data(*gen_all_identities()) @data(*gen_all_identities())
...@@ -279,11 +281,11 @@ class ShibSPTest(CacheIsolationTestCase): ...@@ -279,11 +281,11 @@ class ShibSPTest(CacheIsolationTestCase):
response = client.get(path='/shib-login/', data={}, follow=False, **identity) response = client.get(path='/shib-login/', data={}, follow=False, **identity)
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
mail_input_HTML = '<input class="" id="email" type="email" name="email"' mail_input_html = '<input class="" id="email" type="email" name="email"'
if not identity.get('mail'): if not identity.get('mail'):
self.assertContains(response, mail_input_HTML) self.assertContains(response, mail_input_html)
else: else:
self.assertNotContains(response, mail_input_HTML) self.assertNotContains(response, mail_input_html)
sn_empty = not identity.get('sn') sn_empty = not identity.get('sn')
given_name_empty = not identity.get('givenName') given_name_empty = not identity.get('givenName')
displayname_empty = not identity.get('displayName') displayname_empty = not identity.get('displayName')
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Provides unit tests for SSL based authentication portions Provides unit tests for SSL based authentication portions
of the external_auth app. of the external_auth app.
""" """
# pylint: disable=no-member
import copy import copy
import unittest import unittest
...@@ -14,10 +15,10 @@ from django.core.urlresolvers import reverse ...@@ -14,10 +15,10 @@ from django.core.urlresolvers import reverse
from django.test.client import Client from django.test.client import Client
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.test.utils import override_settings from django.test.utils import override_settings
from external_auth.models import ExternalAuthMap
import external_auth.views
from mock import Mock, patch from mock import Mock, patch
from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
import openedx.core.djangoapps.external_auth.views as external_auth_views
from student.models import CourseEnrollment from student.models import CourseEnrollment
from student.roles import CourseStaffRole from student.roles import CourseStaffRole
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
...@@ -87,7 +88,7 @@ class SSLClientTest(ModuleStoreTestCase): ...@@ -87,7 +88,7 @@ class SSLClientTest(ModuleStoreTestCase):
redirects them to the signup page. redirects them to the signup page.
""" """
with self._create_ssl_request('/') as request: with self._create_ssl_request('/') as request:
response = external_auth.views.ssl_login(request) response = external_auth_views.ssl_login(request)
# Response should contain template for signup form, eamap should have user, and internal # Response should contain template for signup form, eamap should have user, and internal
# auth should not have a user # auth should not have a user
...@@ -127,7 +128,7 @@ class SSLClientTest(ModuleStoreTestCase): ...@@ -127,7 +128,7 @@ class SSLClientTest(ModuleStoreTestCase):
and the user is redirected to slash. and the user is redirected to slash.
""" """
with self._create_ssl_request('/') as request: with self._create_ssl_request('/') as request:
external_auth.views.ssl_login(request) external_auth_views.ssl_login(request)
# Assert our user exists in both eamap and Users, and that we are logged in # Assert our user exists in both eamap and Users, and that we are logged in
try: try:
...@@ -250,7 +251,7 @@ class SSLClientTest(ModuleStoreTestCase): ...@@ -250,7 +251,7 @@ class SSLClientTest(ModuleStoreTestCase):
# Create account, break internal password, and activate account # Create account, break internal password, and activate account
with self._create_ssl_request('/') as request: with self._create_ssl_request('/') as request:
external_auth.views.ssl_login(request) external_auth_views.ssl_login(request)
user = User.objects.get(email=self.USER_EMAIL) user = User.objects.get(email=self.USER_EMAIL)
user.set_password('not autogenerated') user.set_password('not autogenerated')
user.is_active = True user.is_active = True
...@@ -267,7 +268,7 @@ class SSLClientTest(ModuleStoreTestCase): ...@@ -267,7 +268,7 @@ class SSLClientTest(ModuleStoreTestCase):
def test_ssl_decorator_no_certs(self): def test_ssl_decorator_no_certs(self):
"""Make sure no external auth happens without SSL enabled""" """Make sure no external auth happens without SSL enabled"""
dec_mock = external_auth.views.ssl_login_shortcut(self.mock) dec_mock = external_auth_views.ssl_login_shortcut(self.mock)
with self._create_normal_request(self.MOCK_URL) as request: with self._create_normal_request(self.MOCK_URL) as request:
request.user = AnonymousUser() request.user = AnonymousUser()
...@@ -282,7 +283,7 @@ class SSLClientTest(ModuleStoreTestCase): ...@@ -282,7 +283,7 @@ class SSLClientTest(ModuleStoreTestCase):
def test_ssl_login_decorator(self): def test_ssl_login_decorator(self):
"""Create mock function to test ssl login decorator""" """Create mock function to test ssl login decorator"""
dec_mock = external_auth.views.ssl_login_shortcut(self.mock) dec_mock = external_auth_views.ssl_login_shortcut(self.mock)
# Test that anonymous without cert doesn't create authmap # Test that anonymous without cert doesn't create authmap
with self._create_normal_request(self.MOCK_URL) as request: with self._create_normal_request(self.MOCK_URL) as request:
...@@ -312,7 +313,7 @@ class SSLClientTest(ModuleStoreTestCase): ...@@ -312,7 +313,7 @@ class SSLClientTest(ModuleStoreTestCase):
will bypass registration and call retfun. will bypass registration and call retfun.
""" """
dec_mock = external_auth.views.ssl_login_shortcut(self.mock) dec_mock = external_auth_views.ssl_login_shortcut(self.mock)
with self._create_ssl_request(self.MOCK_URL) as request: with self._create_ssl_request(self.MOCK_URL) as request:
dec_mock(request) dec_mock(request)
...@@ -343,7 +344,7 @@ class SSLClientTest(ModuleStoreTestCase): ...@@ -343,7 +344,7 @@ class SSLClientTest(ModuleStoreTestCase):
) )
with self._create_ssl_request('/') as request: with self._create_ssl_request('/') as request:
external_auth.views.ssl_login(request) external_auth_views.ssl_login(request)
user = User.objects.get(email=self.USER_EMAIL) user = User.objects.get(email=self.USER_EMAIL)
CourseEnrollment.enroll(user, course.id) CourseEnrollment.enroll(user, course.id)
course_private_url = '/courses/MITx/999/Robot_Super_Course/courseware' course_private_url = '/courses/MITx/999/Robot_Super_Course/courseware'
...@@ -374,7 +375,7 @@ class SSLClientTest(ModuleStoreTestCase): ...@@ -374,7 +375,7 @@ class SSLClientTest(ModuleStoreTestCase):
) )
with self._create_ssl_request('/') as request: with self._create_ssl_request('/') as request:
external_auth.views.ssl_login(request) external_auth_views.ssl_login(request)
user = User.objects.get(email=self.USER_EMAIL) user = User.objects.get(email=self.USER_EMAIL)
CourseEnrollment.enroll(user, course.id) CourseEnrollment.enroll(user, course.id)
......
"""
External Auth Views
"""
import functools import functools
import json import json
import logging import logging
...@@ -9,8 +12,8 @@ import unicodedata ...@@ -9,8 +12,8 @@ import unicodedata
import urllib import urllib
from textwrap import dedent from textwrap import dedent
from external_auth.models import ExternalAuthMap from openedx.core.djangoapps.external_auth.models import ExternalAuthMap
from external_auth.djangostore import DjangoOpenIDStore from openedx.core.djangoapps.external_auth.djangostore import DjangoOpenIDStore
from django.conf import settings from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME, authenticate, login from django.contrib.auth import REDIRECT_FIELD_NAME, authenticate, login
...@@ -31,11 +34,7 @@ from django.shortcuts import redirect ...@@ -31,11 +34,7 @@ from django.shortcuts import redirect
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from edxmako.shortcuts import render_to_response, render_to_string from edxmako.shortcuts import render_to_response, render_to_string
try: from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
from django.views.decorators.csrf import csrf_exempt
except ImportError:
from django.contrib.csrf.middleware import csrf_exempt
from django.views.decorators.csrf import ensure_csrf_cookie
import django_openid_auth.views as openid_views import django_openid_auth.views as openid_views
from django_openid_auth import auth as openid_auth from django_openid_auth import auth as openid_auth
...@@ -62,7 +61,7 @@ OPENID_DOMAIN_PREFIX = settings.OPENID_DOMAIN_PREFIX ...@@ -62,7 +61,7 @@ OPENID_DOMAIN_PREFIX = settings.OPENID_DOMAIN_PREFIX
@csrf_exempt @csrf_exempt
def default_render_failure(request, def default_render_failure(request, # pylint: disable=unused-argument
message, message,
status=403, status=403,
template_name='extauth_failure.html', template_name='extauth_failure.html',
...@@ -90,7 +89,7 @@ def generate_password(length=12, chars=string.letters + string.digits): ...@@ -90,7 +89,7 @@ def generate_password(length=12, chars=string.letters + string.digits):
@csrf_exempt @csrf_exempt
def openid_login_complete(request, def openid_login_complete(request,
redirect_field_name=REDIRECT_FIELD_NAME, redirect_field_name=REDIRECT_FIELD_NAME, # pylint: disable=unused-argument
render_failure=None): render_failure=None):
"""Complete the openid login process""" """Complete the openid login process"""
...@@ -104,7 +103,7 @@ def openid_login_complete(request, ...@@ -104,7 +103,7 @@ def openid_login_complete(request,
if openid_response.status == SUCCESS: if openid_response.status == SUCCESS:
external_id = openid_response.identity_url external_id = openid_response.identity_url
oid_backend = openid_auth.OpenIDBackend() oid_backend = openid_auth.OpenIDBackend()
details = oid_backend._extract_user_details(openid_response) details = oid_backend._extract_user_details(openid_response) # pylint: disable=protected-access
log.debug('openid success, details=%s', details) log.debug('openid success, details=%s', details)
...@@ -134,6 +133,7 @@ def _external_login_or_signup(request, ...@@ -134,6 +133,7 @@ def _external_login_or_signup(request,
fullname, fullname,
retfun=None): retfun=None):
"""Generic external auth login or signup""" """Generic external auth login or signup"""
# pylint: disable=too-many-statements
# see if we have a map from this external_id to an edX username # see if we have a map from this external_id to an edX username
try: try:
eamap = ExternalAuthMap.objects.get(external_id=external_id, eamap = ExternalAuthMap.objects.get(external_id=external_id,
...@@ -300,7 +300,8 @@ def _signup(request, eamap, retfun=None): ...@@ -300,7 +300,8 @@ def _signup(request, eamap, retfun=None):
# but this only affects username, not fullname # but this only affects username, not fullname
username = re.sub(r'\s', '', _flatten_to_ascii(eamap.external_name), flags=re.UNICODE) username = re.sub(r'\s', '', _flatten_to_ascii(eamap.external_name), flags=re.UNICODE)
context = {'has_extauth_info': True, context = {
'has_extauth_info': True,
'show_signup_immediately': True, 'show_signup_immediately': True,
'extauth_domain': eamap.external_domain, 'extauth_domain': eamap.external_domain,
'extauth_id': eamap.external_id, 'extauth_id': eamap.external_id,
...@@ -343,17 +344,17 @@ def _ssl_dn_extract_info(dn_string): ...@@ -343,17 +344,17 @@ def _ssl_dn_extract_info(dn_string):
full name from the SSL DN string. Return (user,email,fullname) if full name from the SSL DN string. Return (user,email,fullname) if
successful, and None otherwise. successful, and None otherwise.
""" """
ss = re.search('/emailAddress=(.*)@([^/]+)', dn_string) search_string = re.search('/emailAddress=(.*)@([^/]+)', dn_string)
if ss: if search_string:
user = ss.group(1) user = search_string.group(1)
email = "%s@%s" % (user, ss.group(2)) email = "%s@%s" % (user, search_string.group(2))
else: else:
return None raise ValueError
ss = re.search('/CN=([^/]+)/', dn_string) search_string = re.search('/CN=([^/]+)/', dn_string)
if ss: if search_string:
fullname = ss.group(1) fullname = search_string.group(1)
else: else:
return None raise ValueError
return (user, email, fullname) return (user, email, fullname)
...@@ -370,14 +371,14 @@ def ssl_get_cert_from_request(request): ...@@ -370,14 +371,14 @@ def ssl_get_cert_from_request(request):
if not cert: if not cert:
try: try:
# try the direct apache2 SSL key # try the direct apache2 SSL key
cert = request._req.subprocess_env.get(certkey, '') cert = request._req.subprocess_env.get(certkey, '') # pylint: disable=protected-access
except Exception: except Exception: # pylint: disable=broad-except
return '' return ''
return cert return cert
def ssl_login_shortcut(fn): def ssl_login_shortcut(func):
""" """
Python function decorator for login procedures, to allow direct login Python function decorator for login procedures, to allow direct login
based on existing ExternalAuth record and MIT ssl certificate. based on existing ExternalAuth record and MIT ssl certificate.
...@@ -390,19 +391,19 @@ def ssl_login_shortcut(fn): ...@@ -390,19 +391,19 @@ def ssl_login_shortcut(fn):
""" """
if not settings.FEATURES['AUTH_USE_CERTIFICATES']: if not settings.FEATURES['AUTH_USE_CERTIFICATES']:
return fn(*args, **kwargs) return func(*args, **kwargs)
request = args[0] request = args[0]
if request.user and request.user.is_authenticated(): # don't re-authenticate if request.user and request.user.is_authenticated(): # don't re-authenticate
return fn(*args, **kwargs) return func(*args, **kwargs)
cert = ssl_get_cert_from_request(request) cert = ssl_get_cert_from_request(request)
if not cert: # no certificate information - show normal login window if not cert: # no certificate information - show normal login window
return fn(*args, **kwargs) return func(*args, **kwargs)
def retfun(): def retfun():
"""Wrap function again for call by _external_login_or_signup""" """Wrap function again for call by _external_login_or_signup"""
return fn(*args, **kwargs) return func(*args, **kwargs)
(_user, email, fullname) = _ssl_dn_extract_info(cert) (_user, email, fullname) = _ssl_dn_extract_info(cert)
return _external_login_or_signup( return _external_login_or_signup(
...@@ -634,6 +635,9 @@ def get_xrds_url(resource, request): ...@@ -634,6 +635,9 @@ def get_xrds_url(resource, request):
def add_openid_simple_registration(request, response, data): def add_openid_simple_registration(request, response, data):
"""
Add simple registration fields to the response if requested.
"""
sreg_data = {} sreg_data = {}
sreg_request = sreg.SRegRequest.fromOpenIDRequest(request) sreg_request = sreg.SRegRequest.fromOpenIDRequest(request)
sreg_fields = sreg_request.allRequestedFields() sreg_fields = sreg_request.allRequestedFields()
...@@ -655,6 +659,9 @@ def add_openid_simple_registration(request, response, data): ...@@ -655,6 +659,9 @@ def add_openid_simple_registration(request, response, data):
def add_openid_attribute_exchange(request, response, data): def add_openid_attribute_exchange(request, response, data):
"""
Add attribute exchange fields to the response if requested.
"""
try: try:
ax_request = ax.FetchRequest.fromOpenIDRequest(request) ax_request = ax.FetchRequest.fromOpenIDRequest(request)
except ax.AXError: except ax.AXError:
...@@ -691,8 +698,8 @@ def provider_respond(server, request, response, data): ...@@ -691,8 +698,8 @@ def provider_respond(server, request, response, data):
http_response.status_code = webresponse.code http_response.status_code = webresponse.code
# add OpenID headers to response # add OpenID headers to response
for k, v in webresponse.headers.iteritems(): for key, val in webresponse.headers.iteritems():
http_response[k] = v http_response[key] = val
return http_response return http_response
...@@ -744,7 +751,7 @@ def provider_login(request): ...@@ -744,7 +751,7 @@ def provider_login(request):
""" """
OpenID login endpoint OpenID login endpoint
""" """
# pylint: disable=too-many-statements
# make and validate endpoint # make and validate endpoint
endpoint = get_xrds_url('login', request) endpoint = get_xrds_url('login', request)
if not endpoint: if not endpoint:
......
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