def update_or_create(cls, user_id, course_id, **kwargs): """ Creates a course grade in the database. Returns a PersistedCourseGrade object. """ passed = kwargs.pop('passed') if kwargs.get('course_version', None) is None: kwargs['course_version'] = "" grade, _ = cls.objects.update_or_create(user_id=user_id, course_id=course_id, defaults=kwargs) if passed and not grade.passed_timestamp: grade.passed_timestamp = now() grade.save() if not is_testing_environment(): from openedx.adg.lms.applications.helpers import check_and_update_prereq_courses_status_to_completed check_and_update_prereq_courses_status_to_completed( user_id, course_id) if not is_testing_environment(): user = User.objects.get(id=user_id) emit_course_progress_event(user, CourseOverview.get_from_id(course_id), grade=grade) cls._emit_grade_calculated_event(grade) cls._update_cache(course_id, user_id, grade) return grade
def enroll_email(course_id, student_email, auto_enroll=False, email_students=False, email_params=None, language=None): """ Enroll a student by email. `student_email` is student's emails e.g. "*****@*****.**" `auto_enroll` determines what is put in CourseEnrollmentAllowed.auto_enroll if auto_enroll is set, then when the email registers, they will be enrolled in the course automatically. `email_students` determines if student should be notified of action by email. `email_params` parameters used while parsing email templates (a `dict`). `language` is the language used to render the email. returns two EmailEnrollmentState's representing state before and after the action. """ previous_state = EmailEnrollmentState(course_id, student_email) enrollment_obj = None if previous_state.user and User.objects.get(email=student_email).is_active: # if the student is currently unenrolled, don't enroll them in their # previous mode # for now, White Labels use the # "honor" course_mode. Given the change to use "audit" as the default # course_mode in Open edX, we need to be backwards compatible with # how White Labels approach enrollment modes. if CourseMode.is_white_label(course_id): course_mode = CourseMode.HONOR else: course_mode = None if previous_state.enrollment: course_mode = previous_state.mode enrollment_obj = CourseEnrollment.enroll_by_email(student_email, course_id, course_mode) if email_students: email_params['message_type'] = 'enrolled_enroll' email_params['email_address'] = student_email email_params['full_name'] = previous_state.full_name if is_testing_environment(): send_mail_to_student(student_email, email_params, language=language) else: compose_and_send_adg_course_enrollment_invitation_email(student_email, email_params) elif not is_email_retired(student_email): cea, _ = CourseEnrollmentAllowed.objects.get_or_create(course_id=course_id, email=student_email) cea.auto_enroll = auto_enroll cea.save() if email_students: email_params['message_type'] = 'allowed_enroll' email_params['email_address'] = student_email if is_testing_environment(): send_mail_to_student(student_email, email_params, language=language) else: compose_and_send_adg_course_enrollment_invitation_email(student_email, email_params) after_state = EmailEnrollmentState(course_id, student_email) return previous_state, after_state, enrollment_obj
def test_is_testing_environment(monkeypatch): """ Test if the environment is set to test or not """ monkeypatch.setitem(os.environ, 'DJANGO_SETTINGS_MODULE', 'lms.envs.test') assert utils.is_testing_environment() monkeypatch.setitem(os.environ, 'DJANGO_SETTINGS_MODULE', 'cms.envs.test') assert utils.is_testing_environment() monkeypatch.setitem(os.environ, 'DJANGO_SETTINGS_MODULE', 'something_other_than_test') assert not utils.is_testing_environment()
def add_registration_form_defaults(request, form_desc): """ It will add default values in the form if user is invited """ if is_testing_environment(): return form_desc email = request.GET.get('email', '') email = parse.unquote(email) invitation_obj = Invitation.objects.filter( email=email, status=Invitation.PENDING).select_related('business_unit').first() if not invitation_obj: return form_desc business_unit = invitation_obj.business_unit for form in form_desc.fields: if form['name'] == 'email': form['defaultValue'] = email form['restrictions']['readonly'] = True continue if form['name'] == 'is_adg_employee': form['defaultValue'] = bool(business_unit) continue if business_unit and form['name'] == 'company': for option in form['options']: option['default'] = option['value'] == business_unit.title continue return form_desc
def get_courses(user): """ Return courses using core method if environment is test environment else uses customized method for courses list. """ if is_testing_environment(): return get_courses_core(user) return MultilingualCourseGroup.objects.get_user_program_prereq_courses_and_all_non_prereq_courses( user)
def get_login_session_form(request): """Return a description of the login form. This decouples clients from the API definition: if the API decides to modify the form, clients won't need to be updated. See `user_api.helpers.FormDescription` for examples of the JSON-encoded form description. Returns: HttpResponse """ if not is_testing_environment(): from openedx.adg.lms.user_authn_override.login_form import get_login_session_form_override return get_login_session_form_override(request) form_desc = FormDescription("post", reverse("user_api_login_session")) _apply_third_party_auth_overrides(request, form_desc) # Translators: This label appears above a field on the login form # meant to hold the user's email address. email_label = _("Email") # Translators: These instructions appear on the login form, immediately # below a field meant to hold the user's email address. email_instructions = _( "The email address you used to register with {platform_name}").format( platform_name=configuration_helpers.get_value( 'PLATFORM_NAME', settings.PLATFORM_NAME)) form_desc.add_field("email", field_type="email", label=email_label, instructions=email_instructions, restrictions={ "min_length": accounts.EMAIL_MIN_LENGTH, "max_length": accounts.EMAIL_MAX_LENGTH, }) # Translators: This label appears above a field on the login form # meant to hold the user's password. password_label = _(u"Password") form_desc.add_field( "password", label=password_label, field_type="password", restrictions={'max_length': DEFAULT_MAX_PASSWORD_LENGTH}) return form_desc
def get_password_reset_form(): """Return a description of the password reset form. This decouples clients from the API definition: if the API decides to modify the form, clients won't need to be updated. See `user_api.helpers.FormDescription` for examples of the JSON-encoded form description. Returns: HttpResponse """ if not is_testing_environment(): from openedx.adg.lms.user_authn_override.password_reset import get_password_reset_form_override return get_password_reset_form_override() form_desc = FormDescription("post", reverse("password_change_request")) # Translators: This label appears above a field on the password reset # form meant to hold the user's email address. email_label = _(u"Email") # Translators: This example email address is used as a placeholder in # a field on the password reset form meant to hold the user's email address. email_placeholder = _(u"*****@*****.**") # Translators: These instructions appear on the password reset form, # immediately below a field meant to hold the user's email address. # pylint: disable=no-member email_instructions = _( u"The email address you used to register with {platform_name}").format( platform_name=configuration_helpers.get_value( 'PLATFORM_NAME', settings.PLATFORM_NAME)) form_desc.add_field("email", field_type="email", label=email_label, placeholder=email_placeholder, instructions=email_instructions, restrictions={ "min_length": accounts.EMAIL_MIN_LENGTH, "max_length": accounts.EMAIL_MAX_LENGTH, }) return form_desc
def is_referred_by_login_or_register(request): """ Returns True if user is redirected from login or register, otherwise False Arguments: request: HTTP request Returns: Boolean: True if path in HTTP_REFERER contains login or register, otherwise False """ if is_testing_environment(): return True referer = request.META.get('HTTP_REFERER', '') path = parse.urlsplit(referer).path return path in ['/login', '/register']
def save( self, # pylint: disable=arguments-differ use_https=False, token_generator=default_token_generator, request=None, **_kwargs): """ Generates a one-use only link for resetting password and sends to the user. """ for user in self.users_cache: if self.is_account_recovery: if is_testing_environment(): send_password_reset_email_for_user(user, request) else: compose_and_send_adg_password_reset_email(user, request) else: send_account_recovery_email_for_user( user, request, user.account_recovery.secondary_email)
def send_adg_password_reset_success_email(user, request): """ Sends edx default email if the environment is testing. Otherwise, sends adg password reset success email """ from openedx.core.djangoapps.user_authn.views.password_reset import send_password_reset_success_email if is_testing_environment(): send_password_reset_success_email(user, request) return root_url = configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL) context = { 'first_name': get_user_first_name(user), 'signin_link': '{}/login'.format(root_url), } task_send_mandrill_email(MandrillClient.PASSWORD_RESET_SUCCESS, [user.email], context)
def send_account_activation_email(user, profile, registration=None): """ Send adg account activation emails. Args: user (User): User to which email will be sent. profile (UserProfile): Profile of the user. registration (Registration): Registration of the user. Returns: None """ from common.djangoapps.student.views import compose_and_send_activation_email from common.djangoapps.student.models import Registration if is_testing_environment(): compose_and_send_activation_email(user, profile, registration) return if registration is None: registration = Registration.objects.get(user=user) compose_and_send_adg_activation_email(user, registration.activation_key)
def get_extra_course_about_context(request, course): """ Get all the extra context for the course_about page Arguments: request (Request): Request object course (CourseOverview): Course Overview object to add data to the context Returns: dict: Returns an empty dict if it is the testing environment otherwise returns a dict with added context """ if is_testing_environment(): return {} user = request.user course_language_names = [] enrolled_course_group_course = None enroll_popup_message = cannot_enroll_message = '' multilingual_course = MultilingualCourse.objects.all( ).multilingual_course_with_course_id(course.id) if multilingual_course: course_group_courses = multilingual_course.multilingual_course_group.multilingual_courses course_language_codes = course_group_courses.open_multilingual_courses( ).language_codes_with_course_ids() course_language_names = get_language_names_from_codes( course_language_codes) enrolled_course_group_course = course_group_courses.all( ).enrolled_course(user) if enrolled_course_group_course: course_display_name = enrolled_course_group_course.course.display_name_with_default enroll_popup_message = Text( _('Warning: If you wish to change the language of this course, your progress in ' 'the following course(s) will be erased.{line_break}{course_name}' )).format(course_name=course_display_name, line_break=HTML('<br>')) course_enrollment_count = CourseEnrollment.objects.enrollment_counts( course.id).get('total') course_requirements = course_grade = certificate = None if user.is_authenticated: course_requirements = get_pre_requisite_courses_not_completed( user, [course.id]) course_grade = CourseGradeFactory().read(user, course_key=course.id) certificate = GeneratedCertificate.certificate_for_student( user, course.id) has_generated_cert_for_any_other_course_group_course = False if enrolled_course_group_course and enrolled_course_group_course.course != course: has_generated_cert_for_any_other_course_group_course = GeneratedCertificate.objects.filter( course_id=enrolled_course_group_course.course.id, user=user).exists() if has_generated_cert_for_any_other_course_group_course: cannot_enroll_message = _( 'You cannot enroll in this course version as you have already earned a ' 'certificate for another version of this course!') context = { 'course_languages': course_language_names, 'course_requirements': course_requirements, 'total_enrollments': course_enrollment_count, 'self_paced': course.self_paced, 'effort': course.effort, 'is_course_passed': course_grade and getattr(course_grade, 'passed', False), 'has_certificate': certificate, 'has_user_enrolled_in_course_group_courses': bool(enrolled_course_group_course), 'has_generated_cert_for_any_course_group_course': has_generated_cert_for_any_other_course_group_course, 'enroll_popup_message': enroll_popup_message, 'cannot_enroll_message': cannot_enroll_message, } return context
url( r'^courses/{}/search/'.format(settings.COURSE_ID_PATTERN, ), include('openedx.features.course_search.urls'), ), # Learner profile url( r'^u/', include('openedx.features.learner_profile.urls'), ), ] # include all adg lms urls urlpatterns.extend(adg_url_patterns) if not is_testing_environment(): urlpatterns = adg_override_core_url_patterns + urlpatterns if settings.FEATURES.get('ENABLE_TEAMS'): # Teams endpoints urlpatterns += [ url(r'^api/team/', include('lms.djangoapps.teams.api_urls')), url( r'^courses/{}/teams/'.format(settings.COURSE_ID_PATTERN, ), include('lms.djangoapps.teams.urls'), name='teams_endpoints', ), ] # allow course staff to change to student view of courseware if settings.FEATURES.get('ENABLE_MASQUERADE'):
def confirm_email_change(request, key): """ User requested a new e-mail. This is called when the activation link is clicked. We confirm with the old e-mail, and update """ with transaction.atomic(): try: pec = PendingEmailChange.objects.get(activation_key=key) except PendingEmailChange.DoesNotExist: response = render_to_response("invalid_email_key.html", {}) transaction.set_rollback(True) return response user = pec.user address_context = {'old_email': user.email, 'new_email': pec.new_email} if len(User.objects.filter(email=pec.new_email)) != 0: response = render_to_response("email_exists.html", {}) transaction.set_rollback(True) return response use_https = request.is_secure() if settings.FEATURES['ENABLE_MKTG_SITE']: contact_link = marketing_link('CONTACT') else: contact_link = '{protocol}://{site}{link}'.format( protocol='https' if use_https else 'http', site=configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME), link=reverse('contact'), ) site = Site.objects.get_current() message_context = get_base_template_context(site) message_context.update({ 'old_email': user.email, 'new_email': pec.new_email, 'contact_link': contact_link, 'from_address': configuration_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL), }) msg = EmailChangeConfirmation().personalize( recipient=Recipient(user.username, user.email), language=preferences_api.get_user_preference(user, LANGUAGE_KEY), user_context=message_context, ) u_prof = UserProfile.objects.get(user=user) meta = u_prof.get_meta() if 'old_emails' not in meta: meta['old_emails'] = [] meta['old_emails'].append( [user.email, datetime.datetime.now(UTC).isoformat()]) u_prof.set_meta(meta) u_prof.save() # Send it to the old email... try: if is_testing_environment(): ace.send(msg) else: update_all_webinar_registrations_to_new_email( user.email, pec.new_email) compose_and_send_adg_update_email_confirmation( user, message_context) except Exception: # pylint: disable=broad-except log.warning('Unable to send confirmation email to old address', exc_info=True) response = render_to_response("email_change_failed.html", {'email': user.email}) transaction.set_rollback(True) return response user.email = pec.new_email user.save() pec.delete() # And send it to the new email... msg.recipient = Recipient(user.username, pec.new_email) try: if is_testing_environment(): ace.send(msg) else: compose_and_send_adg_update_email_confirmation( user, message_context) except Exception: # pylint: disable=broad-except log.warning('Unable to send confirmation email to new address', exc_info=True) response = render_to_response("email_change_failed.html", {'email': pec.new_email}) transaction.set_rollback(True) return response response = render_to_response("email_change_successful.html", address_context) return response
def do_email_change_request(user, new_email, activation_key=None, secondary_email_change_request=False): """ Given a new email for a user, does some basic verification of the new address and sends an activation message to the new address. If any issues are encountered with verification or sending the message, a ValueError will be thrown. """ # if activation_key is not passing as an argument, generate a random key if not activation_key: activation_key = uuid.uuid4().hex confirm_link = reverse('confirm_email_change', kwargs={ 'key': activation_key, }) if secondary_email_change_request: PendingSecondaryEmailChange.objects.update_or_create( user=user, defaults={ 'new_secondary_email': new_email, 'activation_key': activation_key, }) confirm_link = reverse('activate_secondary_email', kwargs={'key': activation_key}) else: PendingEmailChange.objects.update_or_create(user=user, defaults={ 'new_email': new_email, 'activation_key': activation_key, }) use_https = theming_helpers.get_current_request().is_secure() site = Site.objects.get_current() message_context = get_base_template_context(site) message_context.update({ 'old_email': user.email, 'new_email': new_email, 'confirm_link': '{protocol}://{site}{link}'.format( protocol='https' if use_https else 'http', site=configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME), link=confirm_link, ), }) if secondary_email_change_request: msg = RecoveryEmailCreate().personalize( recipient=Recipient(user.username, new_email), language=preferences_api.get_user_preference(user, LANGUAGE_KEY), user_context=message_context, ) else: msg = EmailChange().personalize( recipient=Recipient(user.username, new_email), language=preferences_api.get_user_preference(user, LANGUAGE_KEY), user_context=message_context, ) try: if is_testing_environment(): ace.send(msg) else: compose_and_send_adg_update_email_verification( user, new_email, use_https, confirm_link) except Exception: from_address = configuration_helpers.get_value( 'email_from_address', settings.DEFAULT_FROM_EMAIL) log.error(u'Unable to send email activation link to user from "%s"', from_address, exc_info=True) raise ValueError( _('Unable to send email activation link. Please try again later.')) if not secondary_email_change_request: # When the email address change is complete, a "edx.user.settings.changed" event will be emitted. # But because changing the email address is multi-step, we also emit an event here so that we can # track where the request was initiated. tracker.emit( SETTING_CHANGE_INITIATED, { "setting": "email", "old": message_context['old_email'], "new": message_context['new_email'], "user_id": user.id, })
def change_enrollment(request, check_access=True): """ Modify the enrollment status for the logged-in user. TODO: This is lms specific and does not belong in common code. The request parameter must be a POST request (other methods return 405) that specifies course_id and enrollment_action parameters. If course_id or enrollment_action is not specified, if course_id is not valid, if enrollment_action is something other than "enroll" or "unenroll", if enrollment_action is "enroll" and enrollment is closed for the course, or if enrollment_action is "unenroll" and the user is not enrolled in the course, a 400 error will be returned. If the user is not logged in, 403 will be returned; it is important that only this case return 403 so the front end can redirect the user to a registration or login page when this happens. This function should only be called from an AJAX request, so the error messages in the responses should never actually be user-visible. Args: request (`Request`): The Django request object Keyword Args: check_access (boolean): If True, we check that an accessible course actually exists for the given course_key before we enroll the student. The default is set to False to avoid breaking legacy code or code with non-standard flows (ex. beta tester invitations), but for any standard enrollment flow you probably want this to be True. Returns: Response """ # Get the user user = request.user # Ensure the user is authenticated if not user.is_authenticated: return HttpResponseForbidden() # Ensure we received a course_id action = request.POST.get("enrollment_action") if 'course_id' not in request.POST: return HttpResponseBadRequest(_("Course id not specified")) try: course_id = CourseKey.from_string(request.POST.get("course_id")) except InvalidKeyError: log.warning( u"User %s tried to %s with invalid course id: %s", user.username, action, request.POST.get("course_id"), ) return HttpResponseBadRequest(_("Invalid course id")) # Allow us to monitor performance of this transaction on a per-course basis since we often roll-out features # on a per-course basis. monitoring_utils.set_custom_attribute('course_id', text_type(course_id)) if action == "enroll": # Make sure the course exists # We don't do this check on unenroll, or a bad course id can't be unenrolled from if not modulestore().has_course(course_id): log.warning(u"User %s tried to enroll in non-existent course %s", user.username, course_id) return HttpResponseBadRequest(_("Course id is invalid")) # Record the user's email opt-in preference if settings.FEATURES.get('ENABLE_MKTG_EMAIL_OPT_IN'): _update_email_opt_in(request, course_id.org) available_modes = CourseMode.modes_for_course_dict(course_id) # Check whether the user is blocked from enrolling in this course # This can occur if the user's IP is on a global blacklist # or if the user is enrolling in a country in which the course # is not available. redirect_url = embargo_api.redirect_if_blocked( course_id, user=user, ip_address=get_ip(request), url=request.path) if redirect_url: return HttpResponse(redirect_url) if CourseEntitlement.check_for_existing_entitlement_and_enroll( user=user, course_run_key=course_id): return HttpResponse( reverse('courseware', args=[six.text_type(course_id)])) # Check that auto enrollment is allowed for this course # (= the course is NOT behind a paywall) if CourseMode.can_auto_enroll(course_id): # Enroll the user using the default mode (audit) # We're assuming that users of the course enrollment table # will NOT try to look up the course enrollment model # by its slug. If they do, it's possible (based on the state of the database) # for no such model to exist, even though we've set the enrollment type # to "audit". try: enroll_mode = CourseMode.auto_enroll_mode( course_id, available_modes) if enroll_mode: if not is_testing_environment() and request.POST.get( 'unenroll_course_group_courses'): course_group = MultilingualCourseGroup.objects.filter( multilingual_courses__course__id=course_id).first( ) if course_group: course_group.unenroll_from_courses(request.user) CourseEnrollment.enroll(user, course_id, check_access=check_access, mode=enroll_mode) if not is_testing_environment(): compose_and_send_adg_course_enrollment_confirmation_email( user, course_id) except Exception: # pylint: disable=broad-except return HttpResponseBadRequest(_("Could not enroll")) # If we have more than one course mode or professional ed is enabled, # then send the user to the choose your track page. # (In the case of no-id-professional/professional ed, this will redirect to a page that # funnels users directly into the verification / payment flow) if CourseMode.has_verified_mode( available_modes) or CourseMode.has_professional_mode( available_modes): return HttpResponse( reverse("course_modes_choose", kwargs={'course_id': text_type(course_id)})) # Otherwise, there is only one mode available (the default) return HttpResponse() elif action == "unenroll": enrollment = CourseEnrollment.get_enrollment(user, course_id) if not enrollment: return HttpResponseBadRequest( _("You are not enrolled in this course")) certificate_info = cert_info(user, enrollment.course_overview) if certificate_info.get('status') in DISABLE_UNENROLL_CERT_STATES: return HttpResponseBadRequest( _("Your certificate prevents you from unenrolling from this course" )) CourseEnrollment.unenroll(user, course_id) REFUND_ORDER.send(sender=None, course_enrollment=enrollment) return HttpResponse() else: return HttpResponseBadRequest(_("Enrollment action is invalid"))
from common.djangoapps.student.helpers import ( AccountValidationError, authenticate_new_user, create_or_set_user_attribute_created_on_site, do_create_account) from common.djangoapps.student.models import (RegistrationCookieConfiguration, UserAttribute, create_comments_service_user, email_exists_or_retired, username_exists_or_retired) from common.djangoapps.student.views import compose_and_send_activation_email from common.djangoapps.third_party_auth import pipeline, provider from common.djangoapps.third_party_auth.saml import SAP_SUCCESSFACTORS_SAML_KEY from common.djangoapps.track import segment from common.djangoapps.util.db import outer_atomic from common.djangoapps.util.json_request import JsonResponse if is_testing_environment(): from openedx.core.djangoapps.user_authn.views.registration_form import RegistrationFormFactory else: from openedx.adg.lms.registration_extension.forms import RegistrationFormFactory from openedx.adg.lms.registration_extension.analytics import emit_registration_event log = logging.getLogger("edx.student") AUDIT_LOG = logging.getLogger("audit") # Used as the name of the user attribute for tracking affiliate registrations REGISTRATION_AFFILIATE_ID = 'registration_affiliate_id' REGISTRATION_UTM_PARAMETERS = { 'utm_source': 'registration_utm_source', 'utm_medium': 'registration_utm_medium', 'utm_campaign': 'registration_utm_campaign', 'utm_term': 'registration_utm_term',
def create_account_with_params(request, params): """ Given a request and a dict of parameters (which may or may not have come from the request), create an account for the requesting user, including creating a comments service user object and sending an activation email. This also takes external/third-party auth into account, updates that as necessary, and authenticates the user for the request's session. Does not return anything. Raises AccountValidationError if an account with the username or email specified by params already exists, or ValidationError if any of the given parameters is invalid for any other reason. Issues with this code: * It is non-transactional except where explicitly wrapped in atomic to alleviate deadlocks and improve performance. This means failures at different places in registration can leave users in inconsistent states. * Third-party auth passwords are not verified. There is a comment that they are unused, but it would be helpful to have a sanity check that they are sane. * The user-facing text is rather unfriendly (e.g. "Username must be a minimum of two characters long" rather than "Please use a username of at least two characters"). * Duplicate email raises a ValidationError (rather than the expected AccountValidationError). Duplicate username returns an inconsistent user message (i.e. "An account with the Public Username '{username}' already exists." rather than "It looks like {username} belongs to an existing account. Try again with a different username.") The two checks occur at different places in the code; as a result, registering with both a duplicate username and email raises only a ValidationError for email only. """ # Copy params so we can modify it; we can't just do dict(params) because if # params is request.POST, that results in a dict containing lists of values params = dict(list(params.items())) # allow to define custom set of required/optional/hidden fields via configuration extra_fields = configuration_helpers.get_value( 'REGISTRATION_EXTRA_FIELDS', getattr(settings, 'REGISTRATION_EXTRA_FIELDS', {})) if is_registration_api_v1(request): if 'confirm_email' in extra_fields: del extra_fields['confirm_email'] # registration via third party (Google, Facebook) using mobile application # doesn't use social auth pipeline (no redirect uri(s) etc involved). # In this case all related info (required for account linking) # is sent in params. # `third_party_auth_credentials_in_api` essentially means 'request # is made from mobile application' third_party_auth_credentials_in_api = 'provider' in params is_third_party_auth_enabled = third_party_auth.is_enabled() if is_third_party_auth_enabled and (pipeline.running(request) or third_party_auth_credentials_in_api): params["password"] = generate_password() # in case user is registering via third party (Google, Facebook) and pipeline has expired, show appropriate # error message if is_third_party_auth_enabled and ('social_auth_provider' in params and not pipeline.running(request)): raise ValidationError({ 'session_expired': [ _(u"Registration using {provider} has timed out.").format( provider=params.get('social_auth_provider')) ] }) extended_profile_fields = configuration_helpers.get_value( 'extended_profile_fields', []) # Can't have terms of service for certain SHIB users, like at Stanford registration_fields = getattr(settings, 'REGISTRATION_EXTRA_FIELDS', {}) tos_required = (registration_fields.get('terms_of_service') != 'hidden' or registration_fields.get('honor_code') != 'hidden') form = AccountCreationForm( data=params, extra_fields=extra_fields, extended_profile_fields=extended_profile_fields, do_third_party_auth=False, tos_required=tos_required, ) custom_form = get_registration_extension_form(data=params) # Perform operations within a transaction that are critical to account creation with outer_atomic(read_committed=True): # first, create the account (user, profile, registration) = do_create_account(form, custom_form) third_party_provider, running_pipeline = _link_user_to_third_party_provider( is_third_party_auth_enabled, third_party_auth_credentials_in_api, user, request, params, ) new_user = authenticate_new_user(request, user.username, form.cleaned_data['password']) django_login(request, new_user) request.session.set_expiry(0) # Sites using multiple languages need to record the language used during registration. # If not, compose_and_send_activation_email will be sent in site's default language only. create_or_set_user_attribute_created_on_site(user, request.site) # Only add a default user preference if user does not already has one. if not preferences_api.has_user_preference(user, LANGUAGE_KEY): preferences_api.set_user_preference(user, LANGUAGE_KEY, get_language()) # Check if system is configured to skip activation email for the current user. skip_email = _skip_activation_email( user, running_pipeline, third_party_provider, ) if skip_email: registration.activate() else: send_account_activation_email(user, profile, registration) if settings.FEATURES.get('ENABLE_DISCUSSION_EMAIL_DIGEST'): try: enable_notifications(user) except Exception: # pylint: disable=broad-except log.exception( u"Enable discussion notifications failed for user {id}.". format(id=user.id)) if is_testing_environment(): _track_user_registration(user, profile, params, third_party_provider) else: emit_registration_event(user) # Announce registration REGISTER_USER.send(sender=None, user=user, registration=registration) create_comments_service_user(user) try: _record_registration_attributions(request, new_user) # Don't prevent a user from registering due to attribution errors. except Exception: # pylint: disable=broad-except log.exception('Error while attributing cookies to user registration.') # TODO: there is no error checking here to see that the user actually logged in successfully, # and is not yet an active user. if new_user is not None: AUDIT_LOG.info(u"Login success on new account creation - {0}".format( new_user.username)) return new_user