def login_user(request): """ AJAX request to log in the user. """ third_party_auth_requested = third_party_auth.is_enabled() and pipeline.running(request) first_party_auth_requested = bool(request.POST.get('email')) or bool(request.POST.get('password')) is_user_third_party_authenticated = False try: if third_party_auth_requested and not first_party_auth_requested: # The user has already authenticated via third-party auth and has not # asked to do first party auth by supplying a username or password. We # now want to put them through the same logging and cookie calculation # logic as with first-party auth. # This nested try is due to us only returning an HttpResponse in this # one case vs. JsonResponse everywhere else. try: user = _do_third_party_auth(request) is_user_third_party_authenticated = True except AuthFailedError as e: return HttpResponse(e.value, content_type="text/plain", status=403) else: user = _get_user_by_email(request) _check_shib_redirect(user) _check_excessive_login_attempts(user) possibly_authenticated_user = user if not is_user_third_party_authenticated: possibly_authenticated_user = _authenticate_first_party(request, user) if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login(): # Important: This call must be made AFTER the user was successfully authenticated. _enforce_password_policy_compliance(request, possibly_authenticated_user) if possibly_authenticated_user is None or not possibly_authenticated_user.is_active: _handle_failed_authentication(user, possibly_authenticated_user) if not is_edly_user_allowed_to_login(request, possibly_authenticated_user): raise AuthFailedError(_('You are not allowed to login on this site.')) _handle_successful_authentication_and_login(possibly_authenticated_user, request) redirect_url = None # The AJAX method calling should know the default destination upon success if is_user_third_party_authenticated: running_pipeline = pipeline.get(request) redirect_url = pipeline.get_complete_url(backend_name=running_pipeline['backend']) response = JsonResponse({ 'success': True, 'redirect_url': redirect_url, }) # Ensure that the external marketing site can # detect that the user is logged in. return set_logged_in_cookies(request, response, possibly_authenticated_user) except AuthFailedError as error: log.exception(error.get_response()) return JsonResponse(error.get_response())
def login_user(request): """ AJAX request to log in the user. """ third_party_auth_requested = third_party_auth.is_enabled() and pipeline.running(request) trumped_by_first_party_auth = bool(request.POST.get('email')) or bool(request.POST.get('password')) was_authenticated_third_party = False try: if third_party_auth_requested and not trumped_by_first_party_auth: # The user has already authenticated via third-party auth and has not # asked to do first party auth by supplying a username or password. We # now want to put them through the same logging and cookie calculation # logic as with first-party auth. # This nested try is due to us only returning an HttpResponse in this # one case vs. JsonResponse everywhere else. try: email_user = _do_third_party_auth(request) was_authenticated_third_party = True except AuthFailedError as e: return HttpResponse(e.value, content_type="text/plain", status=403) else: email_user = _get_user_by_email(request) _check_shib_redirect(email_user) _check_excessive_login_attempts(email_user) _check_forced_password_reset(email_user) possibly_authenticated_user = email_user if not was_authenticated_third_party: possibly_authenticated_user = _authenticate_first_party(request, email_user) if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login(): # Important: This call must be made AFTER the user was successfully authenticated. _enforce_password_policy_compliance(request, possibly_authenticated_user) if possibly_authenticated_user is None or not possibly_authenticated_user.is_active: _handle_failed_authentication(email_user) _handle_successful_authentication_and_login(possibly_authenticated_user, request) redirect_url = None # The AJAX method calling should know the default destination upon success if was_authenticated_third_party: running_pipeline = pipeline.get(request) redirect_url = pipeline.get_complete_url(backend_name=running_pipeline['backend']) response = JsonResponse({ 'success': True, 'redirect_url': redirect_url, }) # Ensure that the external marketing site can # detect that the user is logged in. return set_logged_in_cookies(request, response, possibly_authenticated_user) except AuthFailedError as error: log.exception(error.get_response()) return JsonResponse(error.get_response())
def clean(self): """ Overrides the clean method to allow for the enforcement of password policy requirements. """ cleaned_data = super(PasswordPolicyAwareAdminAuthForm, self).clean() if password_policy_compliance.should_enforce_compliance_on_login(): try: password_policy_compliance.enforce_compliance_on_login(self.user_cache, cleaned_data['password']) except password_policy_compliance.NonCompliantPasswordWarning as e: # Allow login, but warn the user that they will be required to reset their password soon. messages.warning(self.request, e.message) except password_policy_compliance.NonCompliantPasswordException as e: # Prevent the login attempt. raise ValidationError(e.message) return cleaned_data
def clean(self): """ Overrides the clean method to allow for the enforcement of password policy requirements. """ cleaned_data = super().clean() if password_policy_compliance.should_enforce_compliance_on_login(): try: password_policy_compliance.enforce_compliance_on_login(self.user_cache, cleaned_data['password']) except password_policy_compliance.NonCompliantPasswordWarning as e: # Allow login, but warn the user that they will be required to reset their password soon. messages.warning(self.request, HTML(str(e))) except password_policy_compliance.NonCompliantPasswordException as e: # Prevent the login attempt. raise ValidationError(HTML(str(e))) # lint-amnesty, pylint: disable=raise-missing-from return cleaned_data
def clean(self): """ Overrides the clean method to allow for the enforcement of password policy requirements. """ cleaned_data = super(PasswordPolicyAwareAdminAuthForm, self).clean() if password_policy_compliance.should_enforce_compliance_on_login(): try: password_policy_compliance.enforce_compliance_on_login( self.user_cache, cleaned_data['password']) except password_policy_compliance.NonCompliantPasswordWarning as e: # Allow login, but warn the user that they will be required to reset their password soon. messages.warning(self.request, e.message) except password_policy_compliance.NonCompliantPasswordException as e: # Prevent the login attempt. raise ValidationError(e.message) return cleaned_data
def test_should_enforce_compliance_on_login(self): """ Test that if the config is disabled or nonexistent nothing is returned """ # Parameters don't matter for this method as it only tests the config self.assertTrue(should_enforce_compliance_on_login())
def login_user(request): """ AJAX request to log in the user. Arguments: request (HttpRequest) Required params: email, password Optional params: analytics: a JSON-encoded object with additional info to include in the login analytics event. The only supported field is "enroll_course_id" to indicate that the user logged in while enrolling in a particular course. Returns: HttpResponse: 200 if successful. Ex. {'success': true} HttpResponse: 400 if the request failed. Ex. {'success': false, 'value': '{'success': false, 'value: 'Email or password is incorrect.'} HttpResponse: 403 if successful authentication with a third party provider but does not have a linked account. Ex. {'success': false, 'error_code': 'third-party-auth-with-no-linked-account'} Example Usage: POST /login_ajax with POST params `email`, `password` 200 {'success': true} """ _parse_analytics_param_for_course_id(request) third_party_auth_requested = third_party_auth.is_enabled( ) and pipeline.running(request) first_party_auth_requested = bool(request.POST.get('email')) or bool( request.POST.get('password')) is_user_third_party_authenticated = False set_custom_metric('login_user_course_id', request.POST.get('course_id')) try: if third_party_auth_requested and not first_party_auth_requested: # The user has already authenticated via third-party auth and has not # asked to do first party auth by supplying a username or password. We # now want to put them through the same logging and cookie calculation # logic as with first-party auth. # This nested try is due to us only returning an HttpResponse in this # one case vs. JsonResponse everywhere else. try: user = _do_third_party_auth(request) is_user_third_party_authenticated = True set_custom_metric('login_user_tpa_success', True) except AuthFailedError as e: set_custom_metric('login_user_tpa_success', False) set_custom_metric('login_user_tpa_failure_msg', e.value) # user successfully authenticated with a third party provider, but has no linked Open edX account response_content = e.get_response() response_content[ 'error_code'] = 'third-party-auth-with-no-linked-account' return JsonResponse(response_content, status=403) else: user = _get_user_by_email(request) _check_excessive_login_attempts(user) possibly_authenticated_user = user if not is_user_third_party_authenticated: possibly_authenticated_user = _authenticate_first_party( request, user, third_party_auth_requested) if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login( ): # Important: This call must be made AFTER the user was successfully authenticated. _enforce_password_policy_compliance( request, possibly_authenticated_user) if possibly_authenticated_user is None or not possibly_authenticated_user.is_active: _handle_failed_authentication(user, possibly_authenticated_user) _handle_successful_authentication_and_login( possibly_authenticated_user, request) redirect_url = None # The AJAX method calling should know the default destination upon success if is_user_third_party_authenticated: running_pipeline = pipeline.get(request) redirect_url = pipeline.get_complete_url( backend_name=running_pipeline['backend']) response = JsonResponse({ 'success': True, 'redirect_url': redirect_url, }) # Ensure that the external marketing site can # detect that the user is logged in. response = set_logged_in_cookies(request, response, possibly_authenticated_user) set_custom_metric('login_user_auth_failed_error', False) set_custom_metric('login_user_response_status', response.status_code) set_custom_metric('login_user_redirect_url', redirect_url) return response except AuthFailedError as error: log.exception(error.get_response()) response = JsonResponse(error.get_response(), status=400) set_custom_metric('login_user_auth_failed_error', True) set_custom_metric('login_user_response_status', response.status_code) return response
from openedx.core.djangoapps.user_authn.views.login import redirect_to_lms_login from openedx.core.djangoapps.verified_track_content import views as verified_track_content_views from openedx.features.enterprise_support.api import enterprise_enabled from common.djangoapps.student import views as student_views from common.djangoapps.util import views as util_views RESET_COURSE_DEADLINES_NAME = 'reset_course_deadlines' RENDER_XBLOCK_NAME = 'render_xblock' COURSE_DATES_NAME = 'dates' if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'): django_autodiscover() admin.site.site_header = _('LMS Administration') admin.site.site_title = admin.site.site_header if password_policy_compliance.should_enforce_compliance_on_login(): admin.site.login_form = PasswordPolicyAwareAdminAuthForm # Custom error pages # These are used by Django to render these error codes. Do not remove. # pylint: disable=invalid-name handler404 = static_template_view_views.render_404 handler500 = static_template_view_views.render_500 notification_prefs_urls = [ url(r'^notification_prefs/enable/', notification_prefs_views.ajax_enable), url(r'^notification_prefs/disable/', notification_prefs_views.ajax_disable), url(r'^notification_prefs/status/', notification_prefs_views.ajax_status), url( r'^notification_prefs/unsubscribe/(?P<token>[a-zA-Z0-9-_=]+)/',
def login_user(request, api_version='v1'): """ AJAX request to log in the user. Arguments: request (HttpRequest) Required params: email, password Optional params: analytics: a JSON-encoded object with additional info to include in the login analytics event. The only supported field is "enroll_course_id" to indicate that the user logged in while enrolling in a particular course. Returns: HttpResponse: 200 if successful. Ex. {'success': true} HttpResponse: 400 if the request failed. Ex. {'success': false, 'value': '{'success': false, 'value: 'Email or password is incorrect.'} HttpResponse: 403 if successful authentication with a third party provider but does not have a linked account. Ex. {'success': false, 'error_code': 'third-party-auth-with-no-linked-account'} Example Usage: POST /login_ajax with POST params `email`, `password` 200 {'success': true} """ _parse_analytics_param_for_course_id(request) third_party_auth_requested = third_party_auth.is_enabled( ) and pipeline.running(request) first_party_auth_requested = bool(request.POST.get('email')) or bool( request.POST.get('password')) is_user_third_party_authenticated = False set_custom_attribute('login_user_course_id', request.POST.get('course_id')) if is_require_third_party_auth_enabled( ) and not third_party_auth_requested: return HttpResponseForbidden( "Third party authentication is required to login. Username and password were received instead." ) possibly_authenticated_user = None try: if third_party_auth_requested and not first_party_auth_requested: # The user has already authenticated via third-party auth and has not # asked to do first party auth by supplying a username or password. We # now want to put them through the same logging and cookie calculation # logic as with first-party auth. # This nested try is due to us only returning an HttpResponse in this # one case vs. JsonResponse everywhere else. try: user = _do_third_party_auth(request) is_user_third_party_authenticated = True set_custom_attribute('login_user_tpa_success', True) except AuthFailedError as e: set_custom_attribute('login_user_tpa_success', False) set_custom_attribute('login_user_tpa_failure_msg', e.value) if e.error_code: set_custom_attribute('login_error_code', e.error_code) # user successfully authenticated with a third party provider, but has no linked Open edX account response_content = e.get_response() return JsonResponse(response_content, status=403) else: user = _get_user_by_email_or_username(request, api_version) _check_excessive_login_attempts(user) possibly_authenticated_user = user if not is_user_third_party_authenticated: possibly_authenticated_user = _authenticate_first_party( request, user, third_party_auth_requested) if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login( ): # Important: This call must be made AFTER the user was successfully authenticated. _enforce_password_policy_compliance( request, possibly_authenticated_user) check_pwned_password_and_send_track_event.delay( user.id, request.POST.get('password'), user.is_staff) if possibly_authenticated_user is None or not ( possibly_authenticated_user.is_active or settings.MARKETING_EMAILS_OPT_IN): _handle_failed_authentication(user, possibly_authenticated_user) _handle_successful_authentication_and_login( possibly_authenticated_user, request) # The AJAX method calling should know the default destination upon success redirect_url, finish_auth_url = None, '' if third_party_auth_requested: running_pipeline = pipeline.get(request) finish_auth_url = pipeline.get_complete_url( backend_name=running_pipeline['backend']) if is_user_third_party_authenticated: redirect_url = finish_auth_url elif should_redirect_to_authn_microfrontend(): next_url, root_url = get_next_url_for_login_page(request, include_host=True) redirect_url = get_redirect_url_with_host( root_url, enterprise_selection_page(request, possibly_authenticated_user, finish_auth_url or next_url)) response = JsonResponse({ 'success': True, 'redirect_url': redirect_url, }) # Ensure that the external marketing site can # detect that the user is logged in. response = set_logged_in_cookies(request, response, possibly_authenticated_user) set_custom_attribute('login_user_auth_failed_error', False) set_custom_attribute('login_user_response_status', response.status_code) set_custom_attribute('login_user_redirect_url', redirect_url) mark_user_change_as_expected(response, user.id) return response except AuthFailedError as error: response_content = error.get_response() log.exception(response_content) error_code = response_content.get('error_code') if error_code: set_custom_attribute('login_error_code', error_code) email_or_username_key = 'email' if api_version == API_V1 else 'email_or_username' email_or_username = request.POST.get(email_or_username_key, None) email_or_username = possibly_authenticated_user.email \ if possibly_authenticated_user else email_or_username response_content['email'] = email_or_username response = JsonResponse(response_content, status=400) set_custom_attribute('login_user_auth_failed_error', True) set_custom_attribute('login_user_response_status', response.status_code) return response
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.verified_track_content import views as verified_track_content_views from openedx.features.enterprise_support.api import enterprise_enabled from ratelimitbackend import admin from static_template_view import views as static_template_view_views from staticbook import views as staticbook_views from student import views as student_views from track import views as track_views from util import views as util_views if settings.DEBUG or settings.FEATURES.get('ENABLE_DJANGO_ADMIN_SITE'): django_autodiscover() admin.site.site_header = _('LMS Administration') admin.site.site_title = admin.site.site_header if password_policy_compliance.should_enforce_compliance_on_login(): admin.site.login_form = PasswordPolicyAwareAdminAuthForm # Custom error pages # These are used by Django to render these error codes. Do not remove. # pylint: disable=invalid-name handler404 = static_template_view_views.render_404 handler500 = static_template_view_views.render_500 urlpatterns = [ url(r'^$', branding_views.index, name='root'), # Main marketing page, or redirect to courseware url(r'', include('student.urls')), # TODO: Move lms specific student views out of common code url(r'^dashboard/?$', student_views.student_dashboard, name='dashboard'), url(r'^change_enrollment$', student_views.change_enrollment, name='change_enrollment'),
def login_user(request): """ AJAX request to log in the user. """ _parse_analytics_param_for_course_id(request) third_party_auth_requested = third_party_auth.is_enabled( ) and pipeline.running(request) first_party_auth_requested = bool(request.POST.get('email')) or bool( request.POST.get('password')) is_user_third_party_authenticated = False set_custom_metric('login_user_enrollment_action', request.POST.get('enrollment_action')) set_custom_metric('login_user_course_id', request.POST.get('course_id')) try: if third_party_auth_requested and not first_party_auth_requested: # The user has already authenticated via third-party auth and has not # asked to do first party auth by supplying a username or password. We # now want to put them through the same logging and cookie calculation # logic as with first-party auth. # This nested try is due to us only returning an HttpResponse in this # one case vs. JsonResponse everywhere else. try: user = _do_third_party_auth(request) is_user_third_party_authenticated = True set_custom_metric('login_user_tpa_success', True) except AuthFailedError as e: set_custom_metric('login_user_tpa_success', False) set_custom_metric('login_user_tpa_failure_msg', e.value) # user successfully authenticated with a third party provider, but has no linked Open edX account response_content = e.get_response() response_content[ 'error_code'] = 'third-party-auth-with-no-linked-account' return JsonResponse(response_content, status=403) else: user = _get_user_by_email(request) _check_excessive_login_attempts(user) possibly_authenticated_user = user if not is_user_third_party_authenticated: possibly_authenticated_user = _authenticate_first_party( request, user) if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login( ): # Important: This call must be made AFTER the user was successfully authenticated. _enforce_password_policy_compliance( request, possibly_authenticated_user) if possibly_authenticated_user is None or not possibly_authenticated_user.is_active: _handle_failed_authentication(user, possibly_authenticated_user) _handle_successful_authentication_and_login( possibly_authenticated_user, request) redirect_url = None # The AJAX method calling should know the default destination upon success if is_user_third_party_authenticated: running_pipeline = pipeline.get(request) redirect_url = pipeline.get_complete_url( backend_name=running_pipeline['backend']) response = JsonResponse({ 'success': True, 'redirect_url': redirect_url, }) # Ensure that the external marketing site can # detect that the user is logged in. response = set_logged_in_cookies(request, response, possibly_authenticated_user) set_custom_metric('login_user_auth_failed_error', False) set_custom_metric('login_user_response_status', response.status_code) return response except AuthFailedError as error: log.exception(error.get_response()) # original code returned a 200 status code with status=False for errors. This flag # is used for rolling out a transition to using a 400 status code for errors, which # is a breaking-change, but will hopefully be a tolerable breaking-change. status = 400 if UPDATE_LOGIN_USER_ERROR_STATUS_CODE.is_enabled( ) else 200 response = JsonResponse(error.get_response(), status=status) set_custom_metric('login_user_auth_failed_error', True) set_custom_metric('login_user_response_status', response.status_code) return response
def test_should_enforce_compliance_on_login(self): """ Test that if the config is disabled or nonexistent nothing is returned """ # Parameters don't matter for this method as it only tests the config self.assertTrue(should_enforce_compliance_on_login())
def login_user(request): """ AJAX request to log in the user. """ post_data = request.POST.copy() # Decrypt form data if it is encrypted if 'data_token' in request.POST: data_token = request.POST.get('data_token') try: decoded_data = jwt.decode( data_token, settings.EDRAAK_LOGISTRATION_SECRET_KEY, verify=False, algorithms=[settings.EDRAAK_LOGISTRATION_SIGNING_ALGORITHM]) post_data.update(decoded_data) except jwt.ExpiredSignatureError: err_msg = u"The provided data_token has been expired" AUDIT_LOG.warning(err_msg) return JsonResponse({ "success": False, "value": err_msg, }, status=400) except jwt.DecodeError: err_msg = u"Signature verification failed" AUDIT_LOG.warning(err_msg) return JsonResponse({ "success": False, "value": err_msg, }, status=400) except (jwt.InvalidTokenError, ValueError): err_msg = u"Invalid token" AUDIT_LOG.warning(err_msg) return JsonResponse({ "success": False, "value": err_msg, }, status=400) third_party_auth_requested = third_party_auth.is_enabled( ) and pipeline.running(request) trumped_by_first_party_auth = bool(post_data.get('email')) or bool( post_data.get('password')) was_authenticated_third_party = False parent_user = None child_user = None try: if third_party_auth_requested and not trumped_by_first_party_auth: # The user has already authenticated via third-party auth and has not # asked to do first party auth by supplying a username or password. We # now want to put them through the same logging and cookie calculation # logic as with first-party auth. # This nested try is due to us only returning an HttpResponse in this # one case vs. JsonResponse everywhere else. try: email_user = _do_third_party_auth(request) was_authenticated_third_party = True except AuthFailedError as e: return HttpResponse(e.value, content_type="text/plain", status=403) elif 'child_user_id' in post_data: child_user_id = post_data['child_user_id'] try: child_user = User.objects.get(id=child_user_id) except User.DoesNotExist: if settings.FEATURES['SQUELCH_PII_IN_LOGS']: AUDIT_LOG.warning( u"Child login failed - Unknown child user id") else: AUDIT_LOG.warning( u"Child login failed - Unknown child user id: {0}". format(child_user_id)) else: email_user = _get_user_by_email(request, post_data=post_data) if child_user: parent_user = request.user email_user = child_user _check_shib_redirect(email_user) _check_excessive_login_attempts(email_user) _check_forced_password_reset(email_user) # set the user object to child_user object if a child is being logged in possibly_authenticated_user = email_user if not was_authenticated_third_party: possibly_authenticated_user = _authenticate_first_party( request, post_data, email_user) if possibly_authenticated_user and password_policy_compliance.should_enforce_compliance_on_login( ): # Important: This call must be made AFTER the user was successfully authenticated. _enforce_password_policy_compliance( request, post_data, possibly_authenticated_user) if possibly_authenticated_user is None or not possibly_authenticated_user.is_active: _handle_failed_authentication(email_user) _handle_successful_authentication_and_login( possibly_authenticated_user, request, post_data) if parent_user: request.session['parent_user'] = json.dumps({ 'user_id': parent_user.id, 'username': parent_user.username, 'email': parent_user.email, 'name': parent_user.profile.name }) redirect_url = None # The AJAX method calling should know the default destination upon success if was_authenticated_third_party: running_pipeline = pipeline.get(request) redirect_url = pipeline.get_complete_url( backend_name=running_pipeline['backend']) response = JsonResponse({ 'success': True, 'redirect_url': redirect_url, }) # Ensure that the external marketing site can # detect that the user is logged in. return set_logged_in_cookies(request, response, possibly_authenticated_user) except AuthFailedError as error: return JsonResponse(error.get_response())