Commit f7cabf7f by Bertrand Marron

Fix IONISx social provider

parent e3397cf9
...@@ -7,9 +7,9 @@ import re ...@@ -7,9 +7,9 @@ import re
import uuid import uuid
import time import time
import json import json
import requests
from collections import defaultdict from collections import defaultdict
from pytz import UTC from pytz import UTC
from requests import request, ConnectionError
from django.conf import settings from django.conf import settings
from django.contrib.auth import logout, authenticate, login from django.contrib.auth import logout, authenticate, login
...@@ -372,27 +372,31 @@ def register_user(request, extra_context=None): ...@@ -372,27 +372,31 @@ def register_user(request, extra_context=None):
""" """
This view will display the non-modal registration form This view will display the non-modal registration form
""" """
if not settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'): if request.user.is_authenticated():
if request.user.is_authenticated(): return redirect(reverse('dashboard'))
return redirect(reverse('dashboard'))
if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'):
# Redirect to branding to process their certificate if SSL is enabled
# and registration is disabled.
return external_auth.views.redirect_with_get('root', request.GET)
context = { if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
'course_id': request.GET.get('course_id'), # Redirect to IONISx, we don't want the registration form.
'email': '', return external_auth.views.redirect_with_get('/auth/login/portal-oauth2', request.GET, do_reverse=False)
'enrollment_action': request.GET.get('enrollment_action'),
'name': '', if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'):
'running_pipeline': None, # Redirect to branding to process their certificate if SSL is enabled
'platform_name': microsite.get_value( # and registration is disabled.
'platform_name', return external_auth.views.redirect_with_get('root', request.GET)
settings.PLATFORM_NAME
), context = {
'selected_provider': '', 'course_id': request.GET.get('course_id'),
'username': '', 'email': '',
} 'enrollment_action': request.GET.get('enrollment_action'),
'name': '',
'running_pipeline': None,
'platform_name': microsite.get_value(
'platform_name',
settings.PLATFORM_NAME
),
'selected_provider': '',
'username': '',
}
# We save this so, later on, we can determine what course motivated a user's signup # We save this so, later on, we can determine what course motivated a user's signup
# if they actually complete the registration process # if they actually complete the registration process
...@@ -401,8 +405,8 @@ def register_user(request, extra_context=None): ...@@ -401,8 +405,8 @@ 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(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
# selected provider. # selected provider.
...@@ -414,15 +418,7 @@ def register_user(request, extra_context=None): ...@@ -414,15 +418,7 @@ def register_user(request, extra_context=None):
overrides['selected_provider'] = current_provider.NAME overrides['selected_provider'] = current_provider.NAME
context.update(overrides) context.update(overrides)
return render_to_response('register.html', context) return render_to_response('register.html', context)
else:
if request.user.is_authenticated():
if 'course_id' in request.GET:
return redirect("/courses/{0}/about".format(request.GET.get('course_id')))
else:
return redirect('/')
else:
return redirect("/auth/login/portal-oauth2/?auth_entry=login&next={0}".format(urlquote(request.get_full_path())))
def complete_course_mode_info(course_id, enrollment): def complete_course_mode_info(course_id, enrollment):
...@@ -1094,15 +1090,17 @@ def login_user(request, error=""): # pylint: disable-msg=too-many-statements,un ...@@ -1094,15 +1090,17 @@ def login_user(request, error=""): # pylint: disable-msg=too-many-statements,un
}) # TODO: this should be status code 400 # pylint: disable=fixme }) # TODO: this should be status code 400 # pylint: disable=fixme
def logout_portal(user_mail): def logout_portal(request):
try: if request.user.is_authenticated():
user = models.DjangoStorage.user.objects.get(uid=user_mail) user = request.user
response = request('POST', settings.IONISX_AUTH['SYNC_LOGOUT_URL'], social_data = models.DjangoStorage.user.get_social_auth_for_user(user)[0]
params={ 'access_token': user.extra_data['access_token'] }) try:
except ConnectionError as err: requests.post(
log.warning(err) settings.IONISX_AUTH.get('SYNC_LOGOUT_URL'),
except Exception as err: headers={'Authorization': 'Bearer {0}'.format(social_data.extra_data['access_token'])}
log.warning(err) )
except requests.ConnectionError as err:
log.warning(err)
@ensure_csrf_cookie @ensure_csrf_cookie
...@@ -1119,9 +1117,8 @@ def logout_user(request): ...@@ -1119,9 +1117,8 @@ def logout_user(request):
else: else:
user_mail = request.user.email user_mail = request.user.email
logout_portal(request)
logout(request) logout(request)
if user_mail:
logout_portal(user_mail)
if settings.FEATURES.get('AUTH_USE_CAS'): if settings.FEATURES.get('AUTH_USE_CAS'):
target = reverse('cas-logout') target = reverse('cas-logout')
else: else:
......
...@@ -13,8 +13,9 @@ from social.apps.django_app.default import models ...@@ -13,8 +13,9 @@ from social.apps.django_app.default import models
from social.apps.django_app.middleware import SocialAuthExceptionMiddleware from social.apps.django_app.middleware import SocialAuthExceptionMiddleware
from . import pipeline from . import pipeline
from . import portal
log = logging.getLogger('third_party_auth.middleware') log = logging.getLogger(__file__)
class ExceptionMiddleware(SocialAuthExceptionMiddleware): class ExceptionMiddleware(SocialAuthExceptionMiddleware):
"""Custom middleware that handles conditional redirection.""" """Custom middleware that handles conditional redirection."""
...@@ -32,32 +33,36 @@ class PortalSynchronizerMiddleware(object): ...@@ -32,32 +33,36 @@ class PortalSynchronizerMiddleware(object):
"""Custom middleware to synchronize user status of LMS with Portal provider.""" """Custom middleware to synchronize user status of LMS with Portal provider."""
def process_request(self, request): def process_request(self, request):
if not isinstance(request.user, AnonymousUser): if request.user.is_authenticated():
try: user = request.user
email = request.user.email social_auth = models.DjangoStorage.user.get_social_auth_for_user(user)
user = models.DjangoStorage.user.objects.get(uid=email)
response = requests.request('POST', settings.IONISX_AUTH['SYNC_USER_URL'], if len(social_auth) == 1:
params={ 'access_token': user.extra_data['access_token'] }) social_data = social_auth[0]
response = response.json()
if response is None: try:
logout(request) r = requests.get(
response = redirect(request.get_full_path()) settings.IONISX_AUTH.get('USER_DATA_URL'),
response.delete_cookie( headers={'Authorization': 'Bearer {0}'.format(social_data.extra_data['access_token'])}
settings.EDXMKTG_COOKIE_NAME,
path='/', domain=settings.SESSION_COOKIE_DOMAIN,
) )
return response except requests.ConnectionError as err:
if response['updated'] is True: log.warning(err)
log.warning('need update !') return
user = request.user
user.email = response['emails'][0]['email'] user_data = r.json()
user.username = response['username'] if user_data:
user.save() _id = user_data['_id']
email = portal.get_primary_email(user_data['emails'])
profile = UserProfile.objects.get(user=request.user) username = user_data['username']
profile.name = response['name'] name = user_data['name']
profile.save()
except requests.ConnectionError as err: if (user.email != email or user.username != user_data['username']):
log.warning(err) log.info('User {} needs to be updated'.format(_id))
except Exception as err: user.email = email
log.warning(err) user.username = username
user.save()
if user.profile.name != user_data['name']:
log.info('User profile for {} needs to be updated'.format(_id))
user.profile.name = name
user.profile.save()
...@@ -5,35 +5,40 @@ from django.conf import settings ...@@ -5,35 +5,40 @@ from django.conf import settings
from social.backends.oauth import BaseOAuth2 from social.backends.oauth import BaseOAuth2
from social.exceptions import AuthCanceled from social.exceptions import AuthCanceled
def get_primary_email(emails):
for email in emails:
if email['primary'] is True:
return email['email']
return None
class PortalOAuth2(BaseOAuth2): class PortalOAuth2(BaseOAuth2):
"""Portal OAuth2 authentication backend""" """Portal OAuth2 authentication backend"""
name = 'portal-oauth2'
auth_settings = settings.IONISX_AUTH auth_settings = settings.IONISX_AUTH
AUTHORIZATION_URL = auth_settings['AUTHORIZATION_URL']
ACCESS_TOKEN_URL = auth_settings['ACCESS_TOKEN_URL'] name = 'portal-oauth2'
ID_KEY = '_id'
AUTHORIZATION_URL = auth_settings.get('AUTHORIZATION_URL')
ACCESS_TOKEN_URL = auth_settings.get('ACCESS_TOKEN_URL')
ACCESS_TOKEN_METHOD = 'POST' ACCESS_TOKEN_METHOD = 'POST'
REDIRECT_STATE = False REDIRECT_STATE = False
USER_DATA_URL = auth_settings['USER_DATA_URL']
def get_user_id(self, details, response): USER_DATA_URL = auth_settings.get('USER_DATA_URL')
"""Use portal email as unique id"""
if self.setting('USE_UNIQUE_USER_ID', False):
return response['id']
else:
return details['email']
def get_user_details(self, response): def get_user_details(self, response):
"""Return user details from Portal account""" """Return user details from IONISx account"""
return {'username': response.get('username', ''), return {
'email': response.get('emails', '')[0]['email'], 'username': response['username'],
'fullname': response.get('name')} 'email': get_primary_email(response['emails']),
'fullname': response['name']
}
def user_data(self, access_token, *args, **kwargs): def user_data(self, access_token, *args, **kwargs):
"""Loads user data from service""" """Loads user data from service"""
params = self.setting('PROFILE_EXTRA_PARAMS', {}) return self.get_json(
params['access_token'] = access_token self.USER_DATA_URL,
return self.get_json(self.USER_DATA_URL, params=params) headers={'Authorization': 'Bearer {0}'.format(access_token)}
)
def process_error(self, data): def process_error(self, data):
super(PortalOAuth2, self).process_error(data) super(PortalOAuth2, self).process_error(data)
......
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