コード例 #1
0
def get_enterprise_learner_portals(request):
    """
    Gets the formatted portal names and slugs that can be used
    to generate links for enabled enterprise Learner Portals.

    Caches and returns results in/from the user's request session if provided.
    """
    # Prevent a circular import.
    from openedx.features.enterprise_support.api import enterprise_enabled

    if enterprise_enabled():
        # If the key exists return that value
        if 'enterprise_learner_portals' in request.session:
            return json.loads(request.session['enterprise_learner_portals'])

        user = request.user
        # Ordering is important, this is consistent with how we decide on which
        # enterprise_customer is the selected one for an enterprise_customer
        enterprise_learner_portals = [{
            'name': enterprise_customer_user.enterprise_customer.name,
            'slug': enterprise_customer_user.enterprise_customer.slug,
            'logo': enterprise_customer_user.enterprise_customer.branding_configuration.logo.url,
        } for enterprise_customer_user in EnterpriseCustomerUser.objects.filter(
            user_id=user.id, enterprise_customer__enable_learner_portal=True
        ).prefetch_related(
            'enterprise_customer', 'enterprise_customer__branding_configuration'
        ).order_by('-enterprise_customer__active', '-modified')]

        # Cache the result in the user's request session
        request.session['enterprise_learner_portals'] = json.dumps(enterprise_learner_portals)

        return enterprise_learner_portals
    return None
コード例 #2
0
ファイル: test_api.py プロジェクト: TeachAtTUM/edx-platform
 def test_utils_with_enterprise_disabled(self):
     """
     Test that disabling the enterprise integration flag causes
     the utilities to return the expected default values.
     """
     self.assertFalse(enterprise_enabled())
     self.assertEqual(insert_enterprise_pipeline_elements(None), None)
コード例 #3
0
    def get(self, request, username_or_email):
        """
        Returns a list of enrollments for the given user, along with
        information about previous manual enrollment changes.
        """
        try:
            user = User.objects.get(Q(username=username_or_email) | Q(email=username_or_email))
        except User.DoesNotExist:
            return JsonResponse([])

        enrollments = get_enrollments(user.username, include_inactive=True)

        for enrollment in enrollments:
            # Folds the course_details field up into the main JSON object.
            enrollment.update(**enrollment.pop('course_details'))
            course_key = CourseKey.from_string(enrollment['course_id'])
            # Get the all courses modes and replace with existing modes.
            enrollment['course_modes'] = self.get_course_modes(course_key)
            # Add the price of the course's verified mode.
            self.include_verified_mode_info(enrollment, course_key)
            # Add manual enrollment history, if it exists
            enrollment['manual_enrollment'] = self.manual_enrollment_data(enrollment, course_key)

        if enterprise_enabled():
            enterprise_enrollments_by_course_id = self._enterprise_course_enrollments_by_course_id(user)
            for enrollment in enrollments:
                enterprise_course_enrollments = enterprise_enrollments_by_course_id.get(enrollment['course_id'], [])
                enrollment['enterprise_course_enrollments'] = enterprise_course_enrollments

        return JsonResponse(enrollments)
コード例 #4
0
 def test_utils_with_enterprise_disabled(self):
     """
     Test that disabling the enterprise integration flag causes
     the utilities to return the expected default values.
     """
     assert not enterprise_enabled()
     assert insert_enterprise_pipeline_elements(None) is None
コード例 #5
0
 def test_utils_with_enterprise_disabled(self):
     """
     Test that disabling the enterprise integration flag causes
     the utilities to return the expected default values.
     """
     self.assertFalse(enterprise_enabled())
     self.assertEqual(insert_enterprise_pipeline_elements(None), None)
コード例 #6
0
def is_enterprise_learner(user):
    """
    Check if the given user belongs to an enterprise. Cache the value if an enterprise learner is found.

    Arguments:
        user (User): Django User object or Django User object id.

    Returns:
        (bool): True if given user is an enterprise learner.
    """
    # Prevent a circular import.
    from openedx.features.enterprise_support.api import enterprise_enabled

    if not enterprise_enabled():
        return False

    try:
        user_id = int(user)
    except TypeError:
        user_id = user.id
    cached_is_enterprise_key = get_is_enterprise_cache_key(user_id)
    if cache.get(cached_is_enterprise_key):
        return True

    if EnterpriseCustomerUser.objects.filter(user_id=user_id).exists():
        # Cache the enterprise user for one hour.
        cache.set(cached_is_enterprise_key, True, 3600)
        return True

    return False
コード例 #7
0
def get_enterprise_learner_portal(request):
    """
    Gets the formatted portal name and slug that can be used
    to generate a link for an enabled enterprise Learner Portal.

    Caches and returns result in/from the user's request session if provided.
    """
    # Prevent a circular import.
    from openedx.features.enterprise_support.api import enterprise_enabled, enterprise_customer_uuid_for_request

    user = request.user
    # Only cache this if a learner is authenticated (AnonymousUser exists and should not be tracked)

    learner_portal_session_key = 'enterprise_learner_portal'

    if enterprise_enabled() and ENTERPRISE_HEADER_LINKS.is_enabled() and user and user.id:
        # If the key exists return that value
        if learner_portal_session_key in request.session:
            return json.loads(request.session[learner_portal_session_key])

        kwargs = {
            'user_id': user.id,
            'enterprise_customer__enable_learner_portal': True,
        }
        enterprise_customer_uuid = enterprise_customer_uuid_for_request(request)
        if enterprise_customer_uuid:
            kwargs['enterprise_customer__uuid'] = enterprise_customer_uuid

        queryset = EnterpriseCustomerUser.objects.filter(**kwargs).prefetch_related(
            'enterprise_customer',
            'enterprise_customer__branding_configuration',
        )

        if not enterprise_customer_uuid:
            # If the request doesn't help us know which Enterprise Customer UUID to select with,
            # order by the most recently activated/modified customers,
            # so that when we select the first result of the query as the preferred
            # customer, it's the most recently active one.
            queryset = queryset.order_by('-enterprise_customer__active', '-modified')

        preferred_enterprise_customer_user = queryset.first()
        if not preferred_enterprise_customer_user:
            return None

        enterprise_customer = preferred_enterprise_customer_user.enterprise_customer
        learner_portal_data = {
            'name': enterprise_customer.name,
            'slug': enterprise_customer.slug,
            'logo': enterprise_branding_configuration(enterprise_customer).get('logo'),
        }

        # Cache the result in the user's request session
        request.session[learner_portal_session_key] = json.dumps(learner_portal_data)
        return learner_portal_data
    return None
コード例 #8
0
ファイル: signals.py プロジェクト: sliva/edx-platform
def handle_enterprise_learner_passing_grade(sender, user, course_id, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for a learner passing a course, transmit data to relevant integrated channel
    """
    if enterprise_enabled() and is_enterprise_learner(user):
        kwargs = {
            'username': six.text_type(user.username),
            'course_run_id': six.text_type(course_id)
        }

        transmit_single_learner_data.apply_async(kwargs=kwargs)
コード例 #9
0
ファイル: test_api.py プロジェクト: mohit741/edx-platform
 def test_utils_with_enterprise_enabled(self):
     """
     Test that enabling enterprise integration (which is currently on by default) causes the
     the utilities to return the expected values.
     """
     self.assertTrue(enterprise_enabled())
     pipeline = ['abc', 'social_core.pipeline.social_auth.load_extra_data', 'def']
     insert_enterprise_pipeline_elements(pipeline)
     self.assertEqual(pipeline, ['abc',
                                 'enterprise.tpa_pipeline.handle_enterprise_logistration',
                                 'social_core.pipeline.social_auth.load_extra_data',
                                 'def'])
コード例 #10
0
ファイル: test_logout.py プロジェクト: TheMOOCAgency/juniper
    def test_logout_enterprise_target(self, redirect_url, enterprise_target):
        url = '{logout_path}?redirect_url={redirect_url}'.format(
            logout_path=reverse('logout'), redirect_url=redirect_url)
        self.assertTrue(enterprise_enabled())
        response = self.client.get(url, HTTP_HOST='testserver')
        expected = {
            'enterprise_target': enterprise_target,
        }
        self.assertDictContainsSubset(expected, response.context_data)

        if enterprise_target:
            self.assertContains(response, 'We are signing you in.')
コード例 #11
0
ファイル: test_api.py プロジェクト: TeachAtTUM/edx-platform
 def test_utils_with_enterprise_enabled(self):
     """
     Test that enabling enterprise integration (which is currently on by default) causes the
     the utilities to return the expected values.
     """
     self.assertTrue(enterprise_enabled())
     pipeline = ['abc', 'social_core.pipeline.social_auth.load_extra_data', 'def']
     insert_enterprise_pipeline_elements(pipeline)
     self.assertEqual(pipeline, ['abc',
                                 'enterprise.tpa_pipeline.handle_enterprise_logistration',
                                 'social_core.pipeline.social_auth.load_extra_data',
                                 'def'])
コード例 #12
0
ファイル: signals.py プロジェクト: sliva/edx-platform
def handle_enterprise_learner_subsection(sender, user, course_id, subsection_id, subsection_grade, **kwargs):  # pylint: disable=unused-argument
    """
    Listen for an enterprise learner completing a subsection, transmit data to relevant integrated channel.
    """
    if enterprise_enabled() and is_enterprise_learner(user):
        kwargs = {
            'username': str(user.username),
            'course_run_id': str(course_id),
            'subsection_id': str(subsection_id),
            'grade': str(subsection_grade),
        }

        transmit_single_subsection_learner_data.apply_async(kwargs=kwargs)
コード例 #13
0
class SettingsUnitTest(testutil.TestCase):
    """Unit tests for settings management code."""

    # Allow access to protected methods (or module-protected methods) under test.
    # pylint: disable=protected-access
    # Suppress sprurious no-member warning on fakes.
    # pylint: disable=no-member

    def setUp(self):
        super(SettingsUnitTest, self).setUp()
        self.settings = testutil.FakeDjangoSettings(_SETTINGS_MAP)

    def test_apply_settings_adds_exception_middleware(self):
        settings.apply_settings(self.settings)
        for middleware_name in settings._MIDDLEWARE_CLASSES:
            self.assertIn(middleware_name, self.settings.MIDDLEWARE_CLASSES)

    def test_apply_settings_adds_fields_stored_in_session(self):
        settings.apply_settings(self.settings)
        self.assertEqual(settings._FIELDS_STORED_IN_SESSION,
                         self.settings.FIELDS_STORED_IN_SESSION)

    @unittest.skipUnless(testutil.AUTH_FEATURE_ENABLED,
                         'third_party_auth not enabled')
    def test_apply_settings_enables_no_providers_by_default(self):
        # Providers are only enabled via ConfigurationModels in the database
        settings.apply_settings(self.settings)
        self.assertEqual([], provider.Registry.enabled())

    def test_apply_settings_turns_off_raising_social_exceptions(self):
        # Guard against submitting a conf change that's convenient in dev but
        # bad in prod.
        settings.apply_settings(self.settings)
        self.assertFalse(self.settings.SOCIAL_AUTH_RAISE_EXCEPTIONS)

    @unittest.skipUnless(enterprise_enabled(), 'enterprise not enabled')
    def test_enterprise_elements_inserted(self):
        settings.apply_settings(self.settings)
        self.assertIn('enterprise.tpa_pipeline.handle_enterprise_logistration',
                      self.settings.SOCIAL_AUTH_PIPELINE)
コード例 #14
0
def get_enterprise_event_context(user_id, course_id):
    """
    Creates an enterprise context from a `course_id` anf `user_id`.
    Example Returned Context::
        {
            'enterprise_uuid': '1a0fbcbe-49e5-42f1-8e83-4cddfa592f22'
        }
    Arguments:
        user_id: id of user object.
        course_id: id of course object.
    Returns:
        dict: A dictionary representing the enterprise uuid.
    """
    # Prevent a circular import.
    from openedx.features.enterprise_support.api import enterprise_enabled
    from openedx.features.enterprise_support.utils import is_enterprise_learner
    context = {}
    if enterprise_enabled() and is_enterprise_learner(user_id):
        uuids = EnterpriseCourseEnrollment.get_enterprise_uuids_with_user_and_course(str(user_id), str(course_id))
        if uuids:
            context.update({"enterprise_uuid": str(uuids[0])})
    return context
コード例 #15
0
ファイル: views.py プロジェクト: raccoongang/edx-platform
    def post(self, request):
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.

        username = request.data.get('user', request.user.username)
        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        mode = request.data.get('mode')

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode not in (CourseMode.AUDIT, CourseMode.HONOR, None) and not has_api_key_permissions:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": u"User does not have permission to create enrollment with mode [{mode}].".format(
                        mode=mode
                    )
                }
            )

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(
                status=status.HTTP_406_NOT_ACCEPTABLE,
                data={
                    'message': u'The user {} does not exist.'.format(username)
                }
            )

        embargo_response = embargo_api.get_embargo_response(request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message': (u"'{value}' is an invalid enrollment activation status.").format(value=is_active)
                    }
                )

            explicit_linked_enterprise = request.data.get('linked_enterprise_customer')
            if explicit_linked_enterprise and has_api_key_permissions and enterprise_enabled():
                enterprise_api_client = EnterpriseApiServiceClient()
                consent_client = ConsentApiServiceClient()
                try:
                    enterprise_api_client.post_enterprise_course_enrollment(username, unicode(course_id), None)
                except EnterpriseApiException as error:
                    log.exception("An unexpected error occurred while creating the new EnterpriseCourseEnrollment "
                                  "for user [%s] in course run [%s]", username, course_id)
                    raise CourseEnrollmentError(text_type(error))
                kwargs = {
                    'username': username,
                    'course_id': unicode(course_id),
                    'enterprise_customer_uuid': explicit_linked_enterprise,
                }
                consent_client.provide_consent(**kwargs)

            enrollment_attributes = request.data.get('enrollment_attributes')
            enrollment = api.get_enrollment(username, unicode(course_id))
            mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
            missing_attrs = []
            if enrollment_attributes:
                actual_attrs = [
                    u"{namespace}:{name}".format(**attr)
                    for attr in enrollment_attributes
                ]
                missing_attrs = set(REQUIRED_ATTRIBUTES.get(mode, [])) - set(actual_attrs)
            if has_api_key_permissions and (mode_changed or active_changed):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                if len(missing_attrs) > 0:
                    msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
                        mode, REQUIRED_ATTRIBUTES.get(mode)
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                response = api.update_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    # If we are updating enrollment by authorized api caller, we should allow expired modes
                    include_expired=has_api_key_permissions
                )
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes
                )

            cohort_name = request.data.get('cohort')
            if cohort_name is not None:
                cohort = get_cohort_by_name(course_id, cohort_name)
                try:
                    add_user_to_cohort(cohort, user)
                except ValueError:
                    # user already in cohort, probably because they were un-enrolled and re-enrolled
                    log.exception('Cohort re-addition')
            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            log.info('The user [%s] has already been enrolled in course run [%s].', username, course_id)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The [{mode}] course mode is expired or otherwise unavailable for course run [{course_id}]."
                    ).format(mode=mode, course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            log.warning('An enrollment already exists for user [%s] in course run [%s].', username, course_id)
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            log.exception("An error occurred while creating the new course enrollment for user "
                          "[%s] in course run [%s]", username, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
        except CourseUserGroup.DoesNotExist:
            log.exception('Missing cohort [%s] in course run [%s]', cohort_name, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": "An error occured while adding to cohort [%s]" % cohort_name
                })
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            if has_api_key_permissions:
                current_enrollment = api.get_enrollment(username, unicode(course_id))
                audit_log(
                    'enrollment_change_requested',
                    course_id=unicode(course_id),
                    requested_mode=mode,
                    actual_mode=current_enrollment['mode'] if current_enrollment else None,
                    requested_activation=is_active,
                    actual_activation=current_enrollment['is_active'] if current_enrollment else None,
                    user_id=user.id
                )
コード例 #16
0
 def __init__(self):
     """
     We don't need to use this middleware if the Enterprise feature isn't enabled.
     """
     if not api.enterprise_enabled():
         raise MiddlewareNotUsed()
コード例 #17
0
 def __init__(self):
     """
     We don't need to use this middleware if the Enterprise feature isn't enabled.
     """
     if not api.enterprise_enabled():
         raise MiddlewareNotUsed()
コード例 #18
0
ファイル: urls.py プロジェクト: Stanford-Online/edx-platform
    url(r'^debug/show_parameters$', 'debug.views.show_parameters'),
)


# Third-party auth.
if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
    urlpatterns += (
        url(r'', include('third_party_auth.urls')),
        url(r'api/third_party_auth/', include('third_party_auth.api.urls')),
        # NOTE: The following login_oauth_token endpoint is DEPRECATED.
        # Please use the exchange_access_token endpoint instead.
        url(r'^login_oauth_token/(?P<backend>[^/]+)/$', 'student.views.login_oauth_token'),
    )

# Enterprise
if enterprise_enabled():
    urlpatterns += (
        url(r'', include('enterprise.urls')),
    )

# OAuth token exchange
if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
    urlpatterns += (
        url(
            r'^oauth2/login/$',
            LoginWithAccessTokenView.as_view(),
            name="login_with_access_token"
        ),
    )

# Certificates
コード例 #19
0
def login_and_registration_form(request, initial_mode="login"):
    """Render the combined login/registration form, defaulting to login

    This relies on the JS to asynchronously load the actual form from
    the user_api.

    Keyword Args:
        initial_mode (string): Either "login" or "register".

    """
    # Determine the URL to redirect to following login/registration/third_party_auth
    redirect_to = get_next_url_for_login_page(request)

    # If we're already logged in, redirect to the dashboard
    # Note: If the session is valid, we update all logged_in cookies(in particular JWTs)
    #  since Django's SessionAuthentication middleware auto-updates session cookies but not
    #  the other login-related cookies. See ARCH-282 and ARCHBOM-1718
    if request.user.is_authenticated:
        response = redirect(redirect_to)
        response = set_logged_in_cookies(request, response, request.user)
        return response

    # Retrieve the form descriptions from the user API
    form_descriptions = _get_form_descriptions(request)

    # Our ?next= URL may itself contain a parameter 'tpa_hint=x' that we need to check.
    # If present, we display a login page focused on third-party auth with that provider.
    third_party_auth_hint = None
    if '?' in redirect_to:  # lint-amnesty, pylint: disable=too-many-nested-blocks
        try:
            next_args = urllib.parse.parse_qs(
                urllib.parse.urlparse(redirect_to).query)
            if 'tpa_hint' in next_args:
                provider_id = next_args['tpa_hint'][0]
                tpa_hint_provider = third_party_auth.provider.Registry.get(
                    provider_id=provider_id)
                if tpa_hint_provider:
                    if tpa_hint_provider.skip_hinted_login_dialog:
                        # Forward the user directly to the provider's login URL when the provider is configured
                        # to skip the dialog.
                        if initial_mode == "register":
                            auth_entry = pipeline.AUTH_ENTRY_REGISTER
                        else:
                            auth_entry = pipeline.AUTH_ENTRY_LOGIN
                        return redirect(
                            pipeline.get_login_url(provider_id,
                                                   auth_entry,
                                                   redirect_url=redirect_to))
                    third_party_auth_hint = provider_id
                    initial_mode = "hinted_login"
        except (KeyError, ValueError, IndexError) as ex:
            log.exception("Unknown tpa_hint provider: %s", ex)

    # Redirect to authn MFE if it is enabled or user is not an enterprise user or not coming from a SAML IDP.
    saml_provider = False
    running_pipeline = pipeline.get(request)
    enterprise_customer = enterprise_customer_for_request(request)
    if running_pipeline:
        saml_provider, __ = third_party_auth.utils.is_saml_provider(
            running_pipeline.get('backend'), running_pipeline.get('kwargs'))

    if should_redirect_to_authn_microfrontend(
    ) and not enterprise_customer and not saml_provider:

        # This is to handle a case where a logged-in cookie is not present but the user is authenticated.
        # Note: If we don't handle this learner is redirected to authn MFE and then back to dashboard
        # instead of the desired redirect URL (e.g. finish_auth) resulting in learners not enrolling
        # into the courses.
        if request.user.is_authenticated and redirect_to:
            return redirect(redirect_to)

        query_params = request.GET.urlencode()
        url_path = '/{}{}'.format(initial_mode,
                                  '?' + query_params if query_params else '')
        return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path)

    # Account activation message
    account_activation_messages = [{
        'message': message.message,
        'tags': message.tags
    } for message in messages.get_messages(request)
                                   if 'account-activation' in message.tags]

    account_recovery_messages = [{
        'message': message.message,
        'tags': message.tags
    } for message in messages.get_messages(request)
                                 if 'account-recovery' in message.tags]

    # Otherwise, render the combined login/registration page
    context = {
        'data': {
            'login_redirect_url':
            redirect_to,
            'initial_mode':
            initial_mode,
            'third_party_auth':
            third_party_auth_context(request, redirect_to,
                                     third_party_auth_hint),
            'third_party_auth_hint':
            third_party_auth_hint or '',
            'platform_name':
            configuration_helpers.get_value('PLATFORM_NAME',
                                            settings.PLATFORM_NAME),
            'support_link':
            configuration_helpers.get_value('SUPPORT_SITE_LINK',
                                            settings.SUPPORT_SITE_LINK),
            'password_reset_support_link':
            configuration_helpers.get_value(
                'PASSWORD_RESET_SUPPORT_LINK',
                settings.PASSWORD_RESET_SUPPORT_LINK)
            or settings.SUPPORT_SITE_LINK,
            'account_activation_messages':
            account_activation_messages,
            'account_recovery_messages':
            account_recovery_messages,

            # Include form descriptions retrieved from the user API.
            # We could have the JS client make these requests directly,
            # but we include them in the initial page load to avoid
            # the additional round-trip to the server.
            'login_form_desc':
            json.loads(form_descriptions['login']),
            'registration_form_desc':
            json.loads(form_descriptions['registration']),
            'password_reset_form_desc':
            json.loads(form_descriptions['password_reset']),
            'account_creation_allowed':
            configuration_helpers.get_value(
                'ALLOW_PUBLIC_ACCOUNT_CREATION',
                settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION', True)),
            'is_account_recovery_feature_enabled':
            is_secondary_email_feature_enabled(),
            'is_multiple_user_enterprises_feature_enabled':
            is_multiple_user_enterprises_feature_enabled(),
            'enterprise_slug_login_url':
            get_enterprise_slug_login_url(),
            'is_enterprise_enable':
            enterprise_enabled(),
            'is_require_third_party_auth_enabled':
            is_require_third_party_auth_enabled(),
        },
        'login_redirect_url':
        redirect_to,  # This gets added to the query string of the "Sign In" button in header
        'responsive':
        True,
        'allow_iframing':
        True,
        'disable_courseware_js':
        True,
        'combined_login_and_register':
        True,
        'disable_footer':
        not configuration_helpers.get_value(
            'ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER',
            settings.FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION_FOOTER']),
    }

    update_logistration_context_for_enterprise(request, context,
                                               enterprise_customer)

    response = render_to_response('student_account/login_and_register.html',
                                  context)
    handle_enterprise_cookies_for_logistration(request, response, context)

    return response
コード例 #20
0
    def post(self, request):
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.

        username = request.data.get('user', request.user.username)
        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        mode = request.data.get('mode')

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(
                status=status.HTTP_406_NOT_ACCEPTABLE,
                data={
                    'message': u'The user {} does not exist.'.format(username)
                }
            )

        can_vip_enroll = False
        if settings.FEATURES.get('ENABLE_MEMBERSHIP_INTEGRATION'):
            from membership.models import VIPCourseEnrollment
            can_vip_enroll = VIPCourseEnrollment.can_vip_enroll(user, course_id)
        
        is_ecommerce_request = mode not in (CourseMode.AUDIT, CourseMode.HONOR, None)
        if is_ecommerce_request and not has_api_key_permissions and not can_vip_enroll:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": u"User does not have permission to create enrollment with mode [{mode}].".format(
                        mode=mode
                    )
                }
            )

        embargo_response = embargo_api.get_embargo_response(request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message': (u"'{value}' is an invalid enrollment activation status.").format(value=is_active)
                    }
                )

            explicit_linked_enterprise = request.data.get('linked_enterprise_customer')
            if explicit_linked_enterprise and has_api_key_permissions and enterprise_enabled():
                enterprise_api_client = EnterpriseApiServiceClient()
                consent_client = ConsentApiServiceClient()
                try:
                    enterprise_api_client.post_enterprise_course_enrollment(username, unicode(course_id), None)
                except EnterpriseApiException as error:
                    log.exception("An unexpected error occurred while creating the new EnterpriseCourseEnrollment "
                                  "for user [%s] in course run [%s]", username, course_id)
                    raise CourseEnrollmentError(text_type(error))
                kwargs = {
                    'username': username,
                    'course_id': unicode(course_id),
                    'enterprise_customer_uuid': explicit_linked_enterprise,
                }
                consent_client.provide_consent(**kwargs)

            enrollment_attributes = request.data.get('enrollment_attributes')
            enrollment = api.get_enrollment(username, unicode(course_id))
            mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
            missing_attrs = []
            audit_with_order = False
            if enrollment_attributes:
                actual_attrs = [
                    u"{namespace}:{name}".format(**attr)
                    for attr in enrollment_attributes
                ]
                missing_attrs = set(REQUIRED_ATTRIBUTES.get(mode, [])) - set(actual_attrs)
                audit_with_order = mode == 'audit' and 'order:order_number' in actual_attrs
            # Remove audit_with_order when no longer needed - implemented for REV-141
            if has_api_key_permissions and (mode_changed or active_changed or audit_with_order):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                if len(missing_attrs) > 0:
                    msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
                        mode, REQUIRED_ATTRIBUTES.get(mode)
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                response = api.update_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    # If we are updating enrollment by authorized api caller, we should allow expired modes
                    include_expired=has_api_key_permissions
                )
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes,
                    user=user,
                    is_ecommerce_request=is_ecommerce_request
                )

            cohort_name = request.data.get('cohort')
            if cohort_name is not None:
                cohort = get_cohort_by_name(course_id, cohort_name)
                add_user_to_cohort(cohort, user)
            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            log.info('The user [%s] has already been enrolled in course run [%s].', username, course_id)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The [{mode}] course mode is expired or otherwise unavailable for course run [{course_id}]."
                    ).format(mode=mode, course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            log.warning('An enrollment already exists for user [%s] in course run [%s].', username, course_id)
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            log.exception("An error occurred while creating the new course enrollment for user "
                          "[%s] in course run [%s]", username, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
        except CourseUserGroup.DoesNotExist:
            log.exception('Missing cohort [%s] in course run [%s]', cohort_name, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": "An error occured while adding to cohort [%s]" % cohort_name
                })
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            if has_api_key_permissions:
                current_enrollment = api.get_enrollment(username, unicode(course_id))
                audit_log(
                    'enrollment_change_requested',
                    course_id=unicode(course_id),
                    requested_mode=mode,
                    actual_mode=current_enrollment['mode'] if current_enrollment else None,
                    requested_activation=is_active,
                    actual_activation=current_enrollment['is_active'] if current_enrollment else None,
                    user_id=user.id
                )
コード例 #21
0
    url(r'^debug/show_parameters$', debug_views.show_parameters),
]

# Third-party auth.
if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
    urlpatterns += [
        url(r'', include('third_party_auth.urls')),
        url(r'api/third_party_auth/', include('third_party_auth.api.urls')),
        # NOTE: The following login_oauth_token endpoint is DEPRECATED.
        # Please use the exchange_access_token endpoint instead.
        url(r'^login_oauth_token/(?P<backend>[^/]+)/$',
            student_views.login_oauth_token),
    ]

# Enterprise
if enterprise_enabled():
    urlpatterns += [
        url(r'', include('enterprise.urls')),
    ]

# OAuth token exchange
if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
    urlpatterns += [
        url(r'^oauth2/login/$',
            LoginWithAccessTokenView.as_view(),
            name='login_with_access_token'),
    ]

# Certificates
urlpatterns += [
    url(
コード例 #22
0
ファイル: urls.py プロジェクト: IamHDT/thenextdev-edx
        url(r'^debug/show_parameters$', debug_views.show_parameters),
    ]

# Third-party auth.
if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
    urlpatterns += [
        url(r'api/third_party_auth/', include('third_party_auth.api.urls')),
    ]

    if settings.FEATURES.get('ENABLE_VIEWS'):
        urlpatterns += [
            url(r'', include('third_party_auth.urls')),
        ]

# Enterprise
if enterprise_enabled() and settings.FEATURES.get('ENABLE_VIEWS'):
    urlpatterns += [
        url(r'', include('enterprise.urls')),
    ]

# OAuth token exchange
if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
    urlpatterns += [
        url(r'^oauth2/login/$',
            LoginWithAccessTokenView.as_view(),
            name='login_with_access_token'),
    ]

# Certificates
urlpatterns += [
    url(