示例#1
0
    def test_enterprise_customer_for_request_with_session(self):
        """
        Verify enterprise_customer_for_request stores and retrieves data from session appropriately
        """
        dummy_request = mock.MagicMock(session={}, user=self.user)
        enterprise_data = {'name': 'dummy-enterprise-customer', 'uuid': '8dc65e66-27c9-447b-87ff-ede6d66e3a5d'}

        # Verify enterprise customer data fetched from API when it is not available in session
        with mock.patch(
                'openedx.features.enterprise_support.api.enterprise_customer_from_api',
                return_value=enterprise_data
        ):
            self.assertEqual(dummy_request.session.get('enterprise_customer'), None)
            enterprise_customer = enterprise_customer_for_request(dummy_request)
            self.assertEqual(enterprise_customer, enterprise_data)
            self.assertEqual(dummy_request.session.get('enterprise_customer'), enterprise_data)

        # Verify enterprise customer data fetched from session for subsequent calls
        with mock.patch(
                'openedx.features.enterprise_support.api.enterprise_customer_from_api',
                return_value=enterprise_data
        ) as mock_enterprise_customer_from_api, mock.patch(
                'openedx.features.enterprise_support.api.enterprise_customer_from_cache',
                return_value=enterprise_data
        ) as mock_enterprise_customer_from_cache:
            enterprise_customer = enterprise_customer_for_request(dummy_request)
            self.assertEqual(enterprise_customer, enterprise_data)
            self.assertEqual(mock_enterprise_customer_from_api.called, False)
            self.assertEqual(mock_enterprise_customer_from_cache.called, True)
示例#2
0
    def test_enterprise_customer_for_request(
        self,
        mock_registry,
        mock_partial,
        mock_enterprise_customer_model,
    ):
        def mock_get_enterprise_customer(**kwargs):
            uuid = kwargs.get(
                'enterprise_customer_identity_provider__provider_id')
            if uuid:
                return mock.MagicMock(uuid=uuid, user=self.user)
            raise Exception

        dummy_request = mock.MagicMock(session={}, user=self.user)
        mock_enterprise_customer_model.objects.get.side_effect = mock_get_enterprise_customer
        mock_enterprise_customer_model.DoesNotExist = Exception
        mock_partial.return_value = True
        mock_registry.get_from_pipeline.return_value.provider_id = 'real-ent-uuid'

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user.
        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {'real': 'enterprisecustomer'}, 200)
        enterprise_customer = enterprise_customer_for_request(dummy_request)
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        httpretty.reset()

        # Verify that the method `enterprise_customer_for_request` returns no
        # enterprise customer if the enterprise customer API throws 404.
        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {'detail': 'Not found.'}, 404)
        enterprise_customer = enterprise_customer_for_request(dummy_request)
        self.assertIsNone(enterprise_customer)

        httpretty.reset()

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user even if
        # the third-party auth pipeline has no `provider_id`.
        mock_registry.get_from_pipeline.return_value.provider_id = None
        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {'real': 'enterprisecustomer'}, 200)
        enterprise_customer = enterprise_customer_for_request(
            mock.MagicMock(GET={'enterprise_customer': 'real-ent-uuid'},
                           user=self.user))
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user even if
        # the third-party auth pipeline has no `provider_id` but there is
        # enterprise customer UUID in the cookie.
        enterprise_customer = enterprise_customer_for_request(
            mock.MagicMock(GET={},
                           COOKIES={
                               settings.ENTERPRISE_CUSTOMER_COOKIE_NAME:
                               'real-ent-uuid'
                           },
                           user=self.user))
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})
示例#3
0
    def test_enterprise_customer_for_request(
            self,
            mock_registry,
            mock_partial,
            mock_ec_model,
            mock_get_el_data
    ):
        def mock_get_ec(**kwargs):
            uuid = kwargs.get('enterprise_customer_identity_provider__provider_id')
            if uuid:
                return mock.MagicMock(uuid=uuid)
            raise Exception

        mock_ec_model.objects.get.side_effect = mock_get_ec
        mock_ec_model.DoesNotExist = Exception

        mock_partial.return_value = True
        mock_registry.get_from_pipeline.return_value.provider_id = 'real-ent-uuid'

        self.mock_get_enterprise_customer('real-ent-uuid', {"real": "enterprisecustomer"}, 200)

        ec = enterprise_customer_for_request(mock.MagicMock())

        self.assertEqual(ec, {"real": "enterprisecustomer"})

        httpretty.reset()

        self.mock_get_enterprise_customer('real-ent-uuid', {"detail": "Not found."}, 404)

        ec = enterprise_customer_for_request(mock.MagicMock())

        self.assertIsNone(ec)

        mock_registry.get_from_pipeline.return_value.provider_id = None

        httpretty.reset()

        self.mock_get_enterprise_customer('real-ent-uuid', {"real": "enterprisecustomer"}, 200)

        ec = enterprise_customer_for_request(mock.MagicMock(GET={"enterprise_customer": 'real-ent-uuid'}))

        self.assertEqual(ec, {"real": "enterprisecustomer"})

        ec = enterprise_customer_for_request(
            mock.MagicMock(GET={}, COOKIES={settings.ENTERPRISE_CUSTOMER_COOKIE_NAME: 'real-ent-uuid'})
        )

        self.assertEqual(ec, {"real": "enterprisecustomer"})

        mock_get_el_data.return_value = [{'enterprise_customer': {'uuid': 'real-ent-uuid'}}]

        ec = enterprise_customer_for_request(
            mock.MagicMock(GET={}, COOKIES={}, user=mock.MagicMock(is_authenticated=lambda: True), site=1)
        )

        self.assertEqual(ec, {"real": "enterprisecustomer"})
示例#4
0
def _apply_third_party_auth_overrides(request, form_desc):
    """Modify the login form if the user has authenticated with a third-party provider.
    If a user has successfully authenticated with a third-party provider,
    and an email is associated with it then we fill in the email field with readonly property.
    Arguments:
        request (HttpRequest): The request for the registration form, used
            to determine if the user has successfully authenticated
            with a third-party provider.
        form_desc (FormDescription): The registration form description
    """
    if third_party_auth.is_enabled():
        running_pipeline = third_party_auth.pipeline.get(request)
        if running_pipeline:
            current_provider = third_party_auth.provider.Registry.get_from_pipeline(running_pipeline)
            if current_provider and enterprise_customer_for_request(request):
                pipeline_kwargs = running_pipeline.get('kwargs')

                # Details about the user sent back from the provider.
                details = pipeline_kwargs.get('details')
                email = details.get('email', '')

                # override the email field.
                form_desc.override_field_properties(
                    "email",
                    default=email,
                    restrictions={"readonly": "readonly"} if email else {
                        "min_length": accounts.EMAIL_MIN_LENGTH,
                        "max_length": accounts.EMAIL_MAX_LENGTH,
                    }
                )
示例#5
0
def _apply_third_party_auth_overrides(request, form_desc):
    """Modify the login form if the user has authenticated with a third-party provider.
    If a user has successfully authenticated with a third-party provider,
    and an email is associated with it then we fill in the email field with readonly property.
    Arguments:
        request (HttpRequest): The request for the registration form, used
            to determine if the user has successfully authenticated
            with a third-party provider.
        form_desc (FormDescription): The registration form description
    """
    if third_party_auth.is_enabled():
        running_pipeline = third_party_auth.pipeline.get(request)
        if running_pipeline:
            current_provider = third_party_auth.provider.Registry.get_from_pipeline(running_pipeline)
            if current_provider and enterprise_customer_for_request(request):
                pipeline_kwargs = running_pipeline.get('kwargs')

                # Details about the user sent back from the provider.
                details = pipeline_kwargs.get('details')
                email = details.get('email', '')

                # override the email field.
                form_desc.override_field_properties(
                    "email",
                    default=email,
                    restrictions={"readonly": "readonly"} if email else {
                        "min_length": accounts.EMAIL_MIN_LENGTH,
                        "max_length": accounts.EMAIL_MAX_LENGTH,
                    }
                )
示例#6
0
    def get(self, request):  # lint-amnesty, pylint: disable=missing-function-docstring
        if not configuration_helpers.get_value('CONTACT_US_PAGE', True):
            raise Http404

        context = {
            'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME),
            'support_email': configuration_helpers.get_value('CONTACT_EMAIL', settings.CONTACT_EMAIL),
            'custom_fields': settings.ZENDESK_CUSTOM_FIELDS
        }

        # Tag all issues with LMS to distinguish channel which received the request
        tags = ['LMS']

        # Per edX support, we would like to be able to route feedback items by site via tagging
        current_site_name = configuration_helpers.get_value("SITE_NAME")
        if current_site_name:
            current_site_name = current_site_name.replace(".", "_")
            tags.append(f"site_name_{current_site_name}")

        if request.user.is_authenticated:
            context['course_id'] = request.session.get('course_id', '')
            context['user_enrollments'] = CourseEnrollment.enrollments_for_user_with_overviews_preload(request.user)
            enterprise_customer = enterprise_api.enterprise_customer_for_request(request)
            if enterprise_customer:
                tags.append('enterprise_learner')

        context['tags'] = tags

        return render_to_response("support/contact_us.html", context)
示例#7
0
 def process_request(self, request):
     """
     Fill the request with Enterprise-related content.
     """
     if 'enterprise_customer' not in request.session and request.user.is_authenticated:
         request.session[
             'enterprise_customer'] = api.enterprise_customer_for_request(
                 request)
示例#8
0
    def test_enterprise_customer_for_request_with_session(self):
        """
        Verify enterprise_customer_for_request stores and retrieves data from session appropriately
        """
        dummy_request = mock.MagicMock(session={}, user=self.user)
        enterprise_data = {'name': 'dummy-enterprise-customer', 'uuid': '8dc65e66-27c9-447b-87ff-ede6d66e3a5d'}

        # Verify enterprise customer data fetched from API when it is not available in session
        with mock.patch(
                'openedx.features.enterprise_support.api.enterprise_customer_from_api',
                return_value=enterprise_data
        ):
            assert dummy_request.session.get('enterprise_customer') is None
            enterprise_customer = enterprise_customer_for_request(dummy_request)
            assert enterprise_customer == enterprise_data
            assert dummy_request.session.get('enterprise_customer') == enterprise_data

        # Verify enterprise customer data fetched from session for subsequent calls
        with mock.patch(
                'openedx.features.enterprise_support.api.enterprise_customer_from_api',
                return_value=enterprise_data
        ) as mock_enterprise_customer_from_api, mock.patch(
                'openedx.features.enterprise_support.api.enterprise_customer_from_session',
                return_value=enterprise_data
        ) as mock_enterprise_customer_from_session:
            enterprise_customer = enterprise_customer_for_request(dummy_request)
            assert enterprise_customer == enterprise_data
            assert mock_enterprise_customer_from_api.called is False
            assert mock_enterprise_customer_from_session.called is True

        # Verify enterprise customer data fetched from session for subsequent calls
        # with unauthenticated user in SAML case
        del dummy_request.user

        with mock.patch(
            'openedx.features.enterprise_support.api.enterprise_customer_from_api',
            return_value=enterprise_data
        ) as mock_enterprise_customer_from_api, mock.patch(
            'openedx.features.enterprise_support.api.enterprise_customer_from_session',
            return_value=enterprise_data
        ) as mock_enterprise_customer_from_session:
            enterprise_customer = enterprise_customer_for_request(dummy_request)
            assert enterprise_customer == enterprise_data
            assert mock_enterprise_customer_from_api.called is False
            assert mock_enterprise_customer_from_session.called is True
示例#9
0
def get_enterprise_learner_generic_name(request):
    """
    Get a generic name concatenating the Enterprise Customer name and 'Learner'.

    ENT-924: Temporary solution for hiding potentially sensitive SSO names.
    When a more complete solution is put in place, delete this function and all of its uses.
    """
    # Prevent a circular import. This function makes sense to be in this module though. And see function description.
    from openedx.features.enterprise_support.api import enterprise_customer_for_request
    enterprise_customer = enterprise_customer_for_request(request)
    return (enterprise_customer['name'] + 'Learner' if enterprise_customer
            and enterprise_customer['replace_sensitive_sso_username'] else '')
示例#10
0
def get_enterprise_learner_generic_name(request):
    """
    Get a generic name concatenating the Enterprise Customer name and 'Learner'.

    ENT-924: Temporary solution for hiding potentially sensitive SSO names.
    When a more complete solution is put in place, delete this function and all of its uses.
    """
    # Prevent a circular import. This function makes sense to be in this module though. And see function description.
    from openedx.features.enterprise_support.api import enterprise_customer_for_request
    enterprise_customer = enterprise_customer_for_request(request)
    return (
        enterprise_customer['name'] + 'Learner'
        if enterprise_customer and enterprise_customer['replace_sensitive_sso_username']
        else ''
    )
示例#11
0
def enterprise_sidebar_context(request):
    """
    Given the current request, render the HTML of a sidebar for the current
    logistration view that depicts Enterprise-related information.
    """
    enterprise_customer = enterprise_customer_for_request(request)

    if not enterprise_customer:
        return {}

    platform_name = configuration_helpers.get_value('PLATFORM_NAME',
                                                    settings.PLATFORM_NAME)

    if enterprise_customer.branding_configuration.logo:
        enterprise_logo_url = enterprise_customer.branding_configuration.logo.url
    else:
        enterprise_logo_url = ''

    if getattr(enterprise_customer.branding_configuration, 'welcome_message',
               None):
        branded_welcome_template = enterprise_customer.branding_configuration.welcome_message
    else:
        branded_welcome_template = configuration_helpers.get_value(
            'ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE',
            settings.ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE)

    branded_welcome_string = branded_welcome_template.format(
        start_bold=u'<b>',
        end_bold=u'</b>',
        enterprise_name=enterprise_customer.name,
        platform_name=platform_name)

    platform_welcome_template = configuration_helpers.get_value(
        'ENTERPRISE_PLATFORM_WELCOME_TEMPLATE',
        settings.ENTERPRISE_PLATFORM_WELCOME_TEMPLATE)
    platform_welcome_string = platform_welcome_template.format(
        platform_name=platform_name)

    context = {
        'enterprise_name': enterprise_customer.name,
        'enterprise_logo_url': enterprise_logo_url,
        'enterprise_branded_welcome_string': branded_welcome_string,
        'platform_welcome_string': platform_welcome_string,
    }

    return context
示例#12
0
def enterprise_sidebar_context(request):
    """
    Given the current request, render the HTML of a sidebar for the current
    logistration view that depicts Enterprise-related information.
    """
    enterprise_customer = enterprise_customer_for_request(request)

    if not enterprise_customer:
        return {}

    platform_name = configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME)

    if enterprise_customer.branding_configuration.logo:
        enterprise_logo_url = enterprise_customer.branding_configuration.logo.url
    else:
        enterprise_logo_url = ''

    if getattr(enterprise_customer.branding_configuration, 'welcome_message', None):
        branded_welcome_template = enterprise_customer.branding_configuration.welcome_message
    else:
        branded_welcome_template = configuration_helpers.get_value(
            'ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE',
            settings.ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE
        )

    branded_welcome_string = branded_welcome_template.format(
        start_bold=u'<b>',
        end_bold=u'</b>',
        enterprise_name=enterprise_customer.name,
        platform_name=platform_name
    )

    platform_welcome_template = configuration_helpers.get_value(
        'ENTERPRISE_PLATFORM_WELCOME_TEMPLATE',
        settings.ENTERPRISE_PLATFORM_WELCOME_TEMPLATE
    )
    platform_welcome_string = platform_welcome_template.format(platform_name=platform_name)

    context = {
        'enterprise_name': enterprise_customer.name,
        'enterprise_logo_url': enterprise_logo_url,
        'enterprise_branded_welcome_string': branded_welcome_string,
        'platform_welcome_string': platform_welcome_string,
    }

    return context
示例#13
0
def get_enterprise_readonly_account_fields(user):
    """
    Returns a set of account fields that are read-only for enterprise users.
    """
    # TODO circular dependency between enterprise_support.api and enterprise_support.utils
    from openedx.features.enterprise_support.api import enterprise_customer_for_request
    enterprise_customer = enterprise_customer_for_request(get_current_request())

    enterprise_readonly_account_fields = list(settings.ENTERPRISE_READONLY_ACCOUNT_FIELDS)

    # if user has no `UserSocialAuth` record then allow to edit `fullname`
    # whether the `sync_learner_profile_data` is enabled or disabled
    user_social_auth_record = _user_has_social_auth_record(user, enterprise_customer)
    if not user_social_auth_record:
        enterprise_readonly_account_fields.remove('name')

    sync_learner_profile_data = _get_sync_learner_profile_data(enterprise_customer)
    return set(enterprise_readonly_account_fields) if sync_learner_profile_data else set()
示例#14
0
def enterprise_sidebar_context(request):
    """
    Given the current request, render the HTML of a sidebar for the current
    logistration view that depicts Enterprise-related information.
    """
    enterprise_customer = enterprise_customer_for_request(request)

    if not enterprise_customer:
        return {}

    platform_name = configuration_helpers.get_value('PLATFORM_NAME',
                                                    settings.PLATFORM_NAME)

    branding_configuration = enterprise_customer.get('branding_configuration',
                                                     {})
    logo_url = branding_configuration.get('logo', '') if isinstance(
        branding_configuration, dict) else ''

    branded_welcome_template = configuration_helpers.get_value(
        'ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE',
        settings.ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE)

    branded_welcome_string = branded_welcome_template.format(
        start_bold=u'<b>',
        end_bold=u'</b>',
        enterprise_name=enterprise_customer['name'],
        platform_name=platform_name)

    platform_welcome_template = configuration_helpers.get_value(
        'ENTERPRISE_PLATFORM_WELCOME_TEMPLATE',
        settings.ENTERPRISE_PLATFORM_WELCOME_TEMPLATE)
    platform_welcome_string = platform_welcome_template.format(
        platform_name=platform_name)

    context = {
        'enterprise_name': enterprise_customer['name'],
        'enterprise_logo_url': logo_url,
        'enterprise_branded_welcome_string': branded_welcome_string,
        'platform_welcome_string': platform_welcome_string,
    }

    return context
示例#15
0
    def test_enterprise_customer_for_request(self, ec_class_mock,
                                             get_ec_pipeline_mock):
        """
        Test that the correct EnterpriseCustomer, if any, is returned.
        """
        def get_ec_mock(**kwargs):
            by_provider_id_kw = 'enterprise_customer_identity_provider__provider_id'
            provider_id = kwargs.get(by_provider_id_kw, '')
            uuid = kwargs.get('uuid', '')
            if uuid == 'real-uuid' or provider_id == 'real-provider-id':
                return 'this-is-actually-an-enterprise-customer'
            elif uuid == 'not-a-uuid':
                raise ValueError
            else:
                raise Exception

        ec_class_mock.DoesNotExist = Exception
        ec_class_mock.objects.get.side_effect = get_ec_mock

        get_ec_pipeline_mock.return_value = None

        request = mock.MagicMock()
        request.GET.get.return_value = 'real-uuid'
        self.assertEqual(enterprise_customer_for_request(request),
                         'this-is-actually-an-enterprise-customer')
        request.GET.get.return_value = 'not-a-uuid'
        self.assertEqual(enterprise_customer_for_request(request), None)
        request.GET.get.return_value = 'fake-uuid'
        self.assertEqual(enterprise_customer_for_request(request), None)
        request.GET.get.return_value = None
        self.assertEqual(
            enterprise_customer_for_request(request,
                                            tpa_hint='real-provider-id'),
            'this-is-actually-an-enterprise-customer')
        self.assertEqual(
            enterprise_customer_for_request(request,
                                            tpa_hint='fake-provider-id'), None)
        self.assertEqual(
            enterprise_customer_for_request(request, tpa_hint=None), None)

        get_ec_pipeline_mock.return_value = 'also-a-real-enterprise'

        self.assertEqual(enterprise_customer_for_request(request),
                         'also-a-real-enterprise')
示例#16
0
def enterprise_sidebar_context(request):
    """
    Given the current request, render the HTML of a sidebar for the current
    logistration view that depicts Enterprise-related information.
    """
    enterprise_customer = enterprise_customer_for_request(request)

    if not enterprise_customer:
        return {}

    platform_name = configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME)

    branding_configuration = enterprise_customer.get('branding_configuration', {})
    logo_url = branding_configuration.get('logo', '') if isinstance(branding_configuration, dict) else ''

    branded_welcome_template = configuration_helpers.get_value(
        'ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE',
        settings.ENTERPRISE_SPECIFIC_BRANDED_WELCOME_TEMPLATE
    )

    branded_welcome_string = branded_welcome_template.format(
        start_bold=u'<b>',
        end_bold=u'</b>',
        enterprise_name=enterprise_customer['name'],
        platform_name=platform_name
    )

    platform_welcome_template = configuration_helpers.get_value(
        'ENTERPRISE_PLATFORM_WELCOME_TEMPLATE',
        settings.ENTERPRISE_PLATFORM_WELCOME_TEMPLATE
    )
    platform_welcome_string = platform_welcome_template.format(platform_name=platform_name)

    context = {
        'enterprise_name': enterprise_customer['name'],
        'enterprise_logo_url': logo_url,
        'enterprise_branded_welcome_string': branded_welcome_string,
        'platform_welcome_string': platform_welcome_string,
    }

    return context
示例#17
0
    def test_enterprise_customer_for_request(self, ec_class_mock, get_ec_pipeline_mock):
        """
        Test that the correct EnterpriseCustomer, if any, is returned.
        """
        def get_ec_mock(**kwargs):
            by_provider_id_kw = 'enterprise_customer_identity_provider__provider_id'
            provider_id = kwargs.get(by_provider_id_kw, '')
            uuid = kwargs.get('uuid', '')
            if uuid == 'real-uuid' or provider_id == 'real-provider-id':
                return 'this-is-actually-an-enterprise-customer'
            elif uuid == 'not-a-uuid':
                raise ValueError
            else:
                raise Exception

        ec_class_mock.DoesNotExist = Exception
        ec_class_mock.objects.get.side_effect = get_ec_mock

        get_ec_pipeline_mock.return_value = None

        request = mock.MagicMock()
        request.GET.get.return_value = 'real-uuid'
        self.assertEqual(enterprise_customer_for_request(request), 'this-is-actually-an-enterprise-customer')
        request.GET.get.return_value = 'not-a-uuid'
        self.assertEqual(enterprise_customer_for_request(request), None)
        request.GET.get.return_value = 'fake-uuid'
        self.assertEqual(enterprise_customer_for_request(request), None)
        request.GET.get.return_value = None
        self.assertEqual(
            enterprise_customer_for_request(request, tpa_hint='real-provider-id'),
            'this-is-actually-an-enterprise-customer'
        )
        self.assertEqual(enterprise_customer_for_request(request, tpa_hint='fake-provider-id'), None)
        self.assertEqual(enterprise_customer_for_request(request, tpa_hint=None), None)

        get_ec_pipeline_mock.return_value = 'also-a-real-enterprise'

        self.assertEqual(enterprise_customer_for_request(request), 'also-a-real-enterprise')
示例#18
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: We check for the existence of login-related cookies in addition to is_authenticated
    #  since Django's SessionAuthentication middleware auto-updates session cookies but not
    #  the other login-related cookies. See ARCH-282.
    if request.user.is_authenticated and are_logged_in_cookies_set(request):
        return redirect(redirect_to)

    # 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:
        try:
            next_args = six.moves.urllib.parse.parse_qs(six.moves.urllib.parse.urlparse(redirect_to).query)
            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(u"Unknown tpa_hint provider: %s", ex)

    # We are defaulting to true because all themes should now be using the newer page.
    if is_request_in_themed_site() and not configuration_helpers.get_value('ENABLE_COMBINED_LOGIN_REGISTRATION', True):
        if initial_mode == "login":
            return old_login_view(request)
        elif initial_mode == "register":
            return old_register_view(request)

    # 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()
        },
        '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']
        ),
    }

    enterprise_customer = enterprise_customer_for_request(request)
    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
示例#19
0
    def _apply_third_party_auth_overrides(self, request, form_desc):
        """Modify the registration form if the user has authenticated with a third-party provider.

        If a user has successfully authenticated with a third-party provider,
        but does not yet have an account with EdX, we want to fill in
        the registration form with any info that we get from the
        provider.

        This will also hide the password field, since we assign users a default
        (random) password on the assumption that they will be using
        third-party auth to log in.

        Arguments:
            request (HttpRequest): The request for the registration form, used
                to determine if the user has successfully authenticated
                with a third-party provider.

            form_desc (FormDescription): The registration form description

        """
        if third_party_auth.is_enabled():
            running_pipeline = third_party_auth.pipeline.get(request)
            if running_pipeline:
                current_provider = third_party_auth.provider.Registry.get_from_pipeline(
                    running_pipeline)

                if current_provider:
                    # Override username / email / full name
                    field_overrides = current_provider.get_register_form_data(
                        running_pipeline.get('kwargs'))

                    # When the TPA Provider is configured to skip the registration form and we are in an
                    # enterprise context, we need to hide all fields except for terms of service and
                    # ensure that the user explicitly checks that field.
                    hide_registration_fields_except_tos = (
                        current_provider.skip_registration_form
                        and enterprise_customer_for_request(request))

                    for field_name in self.DEFAULT_FIELDS + self.EXTRA_FIELDS:
                        if field_name in field_overrides:
                            form_desc.override_field_properties(
                                field_name,
                                default=field_overrides[field_name])

                            if (field_name
                                    not in ['terms_of_service', 'honor_code']
                                    and field_overrides[field_name]
                                    and hide_registration_fields_except_tos):

                                form_desc.override_field_properties(
                                    field_name,
                                    field_type="hidden",
                                    label="",
                                    instructions="",
                                )

                    # Hide the password field
                    form_desc.override_field_properties("password",
                                                        default="",
                                                        field_type="hidden",
                                                        required=False,
                                                        label="",
                                                        instructions="",
                                                        restrictions={})
                    # used to identify that request is running third party social auth
                    form_desc.add_field(
                        "social_auth_provider",
                        field_type="hidden",
                        label="",
                        default=current_provider.name
                        if current_provider.name else "Third Party",
                        required=False,
                    )
示例#20
0
    def test_enterprise_customer_for_request(self, mock_registry, mock_partial,
                                             mock_ec_model, mock_get_el_data):
        def mock_get_ec(**kwargs):
            uuid = kwargs.get(
                'enterprise_customer_identity_provider__provider_id')
            if uuid:
                return mock.MagicMock(uuid=uuid)
            raise Exception

        mock_ec_model.objects.get.side_effect = mock_get_ec
        mock_ec_model.DoesNotExist = Exception

        mock_partial.return_value = True
        mock_registry.get_from_pipeline.return_value.provider_id = 'real-ent-uuid'

        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {"real": "enterprisecustomer"}, 200)

        ec = enterprise_customer_for_request(mock.MagicMock())

        self.assertEqual(ec, {"real": "enterprisecustomer"})

        httpretty.reset()

        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {"detail": "Not found."}, 404)

        ec = enterprise_customer_for_request(mock.MagicMock())

        self.assertIsNone(ec)

        mock_registry.get_from_pipeline.return_value.provider_id = None

        httpretty.reset()

        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {"real": "enterprisecustomer"}, 200)

        ec = enterprise_customer_for_request(
            mock.MagicMock(GET={"enterprise_customer": 'real-ent-uuid'}))

        self.assertEqual(ec, {"real": "enterprisecustomer"})

        ec = enterprise_customer_for_request(
            mock.MagicMock(GET={},
                           COOKIES={
                               settings.ENTERPRISE_CUSTOMER_COOKIE_NAME:
                               'real-ent-uuid'
                           }))

        self.assertEqual(ec, {"real": "enterprisecustomer"})

        mock_get_el_data.return_value = [{
            'enterprise_customer': {
                'uuid': 'real-ent-uuid'
            }
        }]

        ec = enterprise_customer_for_request(
            mock.MagicMock(GET={},
                           COOKIES={},
                           user=mock.MagicMock(is_authenticated=lambda: True),
                           site=1))

        self.assertEqual(ec, {"real": "enterprisecustomer"})
示例#21
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
    if request.user.is_authenticated:
        return redirect(redirect_to)

    if settings.FEATURES.get("ENABLE_EDRAAK_LOGISTRATION",
                             False) and settings.PROGS_URLS:
        redirect_to = get_next_url_for_progs_login_page(request, initial_mode)
        return redirect(redirect_to)

    # 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:
        try:
            next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query)
            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.error("Unknown tpa_hint provider: %s", ex)

    # If this is a themed site, revert to the old login/registration pages.
    # We need to do this for now to support existing themes.
    # Themed sites can use the new logistration page by setting
    # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their
    # configuration settings.
    if is_request_in_themed_site() and not configuration_helpers.get_value(
            'ENABLE_COMBINED_LOGIN_REGISTRATION', False):
        if initial_mode == "login":
            return old_login_view(request)
        elif initial_mode == "register":
            return old_register_view(request)

    # Allow external auth to intercept and handle the request
    ext_auth_response = _external_auth_intercept(request, initial_mode)
    if ext_auth_response is not None:
        return ext_auth_response

    # 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]

    # 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,

            # 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))
        },
        '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']),
    }

    enterprise_customer = enterprise_customer_for_request(request)
    update_logistration_context_for_enterprise(request, context,
                                               enterprise_customer)

    # Edraak: add (origin) option to the context if applicable
    edraak_update_origin(
        request, context)  # Warning: This modifies `context['data']['origin']`

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

    return response
示例#22
0
    def _apply_third_party_auth_overrides(self, request, form_desc):
        """Modify the registration form if the user has authenticated with a third-party provider.
        If a user has successfully authenticated with a third-party provider,
        but does not yet have an account with EdX, we want to fill in
        the registration form with any info that we get from the
        provider.
        This will also hide the password field, since we assign users a default
        (random) password on the assumption that they will be using
        third-party auth to log in.
        Arguments:
            request (HttpRequest): The request for the registration form, used
                to determine if the user has successfully authenticated
                with a third-party provider.
            form_desc (FormDescription): The registration form description
        """
        if third_party_auth.is_enabled():
            running_pipeline = third_party_auth.pipeline.get(request)
            if running_pipeline:
                current_provider = third_party_auth.provider.Registry.get_from_pipeline(running_pipeline)

                if current_provider:
                    # Override username / email / full name
                    field_overrides = current_provider.get_register_form_data(
                        running_pipeline.get('kwargs')
                    )

                    # When the TPA Provider is configured to skip the registration form and we are in an
                    # enterprise context, we need to hide all fields except for terms of service and
                    # ensure that the user explicitly checks that field.
                    hide_registration_fields_except_tos = (
                        (
                            current_provider.skip_registration_form and enterprise_customer_for_request(request)
                        ) or current_provider.sync_learner_profile_data
                    )

                    for field_name in self.DEFAULT_FIELDS + self.EXTRA_FIELDS:
                        if field_name in field_overrides:
                            form_desc.override_field_properties(
                                field_name, default=field_overrides[field_name]
                            )

                            if (field_name not in ['terms_of_service', 'honor_code']
                                    and field_overrides[field_name]
                                    and hide_registration_fields_except_tos):

                                form_desc.override_field_properties(
                                    field_name,
                                    field_type="hidden",
                                    label="",
                                    instructions="",
                                )

                    # Hide the password field
                    form_desc.override_field_properties(
                        "password",
                        default="",
                        field_type="hidden",
                        required=False,
                        label="",
                        instructions="",
                        restrictions={}
                    )
                    # used to identify that request is running third party social auth
                    form_desc.add_field(
                        "social_auth_provider",
                        field_type="hidden",
                        label="",
                        default=current_provider.name if current_provider.name else "Third Party",
                        required=False,
                    )
示例#23
0
    def get(self, request, course_id, error=None):  # lint-amnesty, pylint: disable=too-many-statements
        """Displays the course mode choice page.

        Args:
            request (`Request`): The Django Request object.
            course_id (unicode): The slash-separated course key.

        Keyword Args:
            error (unicode): If provided, display this error message
                on the page.

        Returns:
            Response

        """
        course_key = CourseKey.from_string(course_id)

        # Check whether the user has access to this course
        # based on country access rules.
        embargo_redirect = embargo_api.redirect_if_blocked(
            course_key,
            user=request.user,
            ip_address=get_client_ip(request)[0],
            url=request.path)
        if embargo_redirect:
            return redirect(embargo_redirect)

        enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(
            request.user, course_key)

        increment('track-selection.{}.{}'.format(
            enrollment_mode, 'active' if is_active else 'inactive'))
        increment('track-selection.views')

        if enrollment_mode is None:
            LOG.info(
                'Rendering track selection for unenrolled user, referred by %s',
                request.META.get('HTTP_REFERER'))

        modes = CourseMode.modes_for_course_dict(course_key)
        ecommerce_service = EcommerceService()

        # We assume that, if 'professional' is one of the modes, it should be the *only* mode.
        # If there are both modes, default to 'no-id-professional'.
        has_enrolled_professional = (
            CourseMode.is_professional_slug(enrollment_mode) and is_active)
        if CourseMode.has_professional_mode(
                modes) and not has_enrolled_professional:
            purchase_workflow = request.GET.get("purchase_workflow", "single")
            redirect_url = IDVerificationService.get_verify_location(
                course_id=course_key)
            if ecommerce_service.is_enabled(request.user):
                professional_mode = modes.get(
                    CourseMode.NO_ID_PROFESSIONAL_MODE) or modes.get(
                        CourseMode.PROFESSIONAL)
                if purchase_workflow == "single" and professional_mode.sku:
                    redirect_url = ecommerce_service.get_checkout_page_url(
                        professional_mode.sku)
                if purchase_workflow == "bulk" and professional_mode.bulk_sku:
                    redirect_url = ecommerce_service.get_checkout_page_url(
                        professional_mode.bulk_sku)
            return redirect(redirect_url)

        course = modulestore().get_course(course_key)

        # If there isn't a verified mode available, then there's nothing
        # to do on this page.  Send the user to the dashboard.
        if not CourseMode.has_verified_mode(modes):
            return self._redirect_to_course_or_dashboard(
                course, course_key, request.user)

        # If a user has already paid, redirect them to the dashboard.
        if is_active and (enrollment_mode in CourseMode.VERIFIED_MODES +
                          [CourseMode.NO_ID_PROFESSIONAL_MODE]):
            return self._redirect_to_course_or_dashboard(
                course, course_key, request.user)

        donation_for_course = request.session.get("donation_for_course", {})
        chosen_price = donation_for_course.get(str(course_key), None)

        if CourseEnrollment.is_enrollment_closed(request.user, course):
            locale = to_locale(get_language())
            enrollment_end_date = format_datetime(course.enrollment_end,
                                                  'short',
                                                  locale=locale)
            params = six.moves.urllib.parse.urlencode(
                {'course_closed': enrollment_end_date})
            return redirect('{}?{}'.format(reverse('dashboard'), params))

        # When a credit mode is available, students will be given the option
        # to upgrade from a verified mode to a credit mode at the end of the course.
        # This allows students who have completed photo verification to be eligible
        # for university credit.
        # Since credit isn't one of the selectable options on the track selection page,
        # we need to check *all* available course modes in order to determine whether
        # a credit mode is available.  If so, then we show slightly different messaging
        # for the verified track.
        has_credit_upsell = any(
            CourseMode.is_credit_mode(mode)
            for mode in CourseMode.modes_for_course(course_key,
                                                    only_selectable=False))
        course_id = str(course_key)
        gated_content = ContentTypeGatingConfig.enabled_for_enrollment(
            user=request.user, course_key=course_key)
        context = {
            "course_modes_choose_url":
            reverse("course_modes_choose", kwargs={'course_id': course_id}),
            "modes":
            modes,
            "has_credit_upsell":
            has_credit_upsell,
            "course_name":
            course.display_name_with_default,
            "course_org":
            course.display_org_with_default,
            "course_num":
            course.display_number_with_default,
            "chosen_price":
            chosen_price,
            "error":
            error,
            "responsive":
            True,
            "nav_hidden":
            True,
            "content_gating_enabled":
            gated_content,
            "course_duration_limit_enabled":
            CourseDurationLimitConfig.enabled_for_enrollment(
                request.user, course),
        }
        context.update(
            get_experiment_user_metadata_context(
                course,
                request.user,
            ))

        title_content = ''
        if enrollment_mode:
            title_content = _(
                "Congratulations!  You are now enrolled in {course_name}"
            ).format(course_name=course.display_name_with_default)

        context["title_content"] = title_content

        if "verified" in modes:
            verified_mode = modes["verified"]
            context["suggested_prices"] = [
                decimal.Decimal(x.strip())
                for x in verified_mode.suggested_prices.split(",")
                if x.strip()
            ]
            price_before_discount = verified_mode.min_price
            course_price = price_before_discount
            enterprise_customer = enterprise_customer_for_request(request)
            LOG.info(
                '[e-commerce calculate API] Going to hit the API for user [%s] linked to [%s] enterprise',
                request.user.username,
                enterprise_customer.get('name') if isinstance(
                    enterprise_customer, dict) else None  # Test Purpose
            )
            if enterprise_customer and verified_mode.sku:
                course_price = get_course_final_price(request.user,
                                                      verified_mode.sku,
                                                      price_before_discount)

            context["currency"] = verified_mode.currency.upper()
            context["currency_symbol"] = get_currency_symbol(
                verified_mode.currency.upper())
            context["min_price"] = course_price
            context["verified_name"] = verified_mode.name
            context["verified_description"] = verified_mode.description
            # if course_price is equal to price_before_discount then user doesn't entitle to any discount.
            if course_price != price_before_discount:
                context["price_before_discount"] = price_before_discount

            if verified_mode.sku:
                context[
                    "use_ecommerce_payment_flow"] = ecommerce_service.is_enabled(
                        request.user)
                context[
                    "ecommerce_payment_page"] = ecommerce_service.payment_page_url(
                    )
                context["sku"] = verified_mode.sku
                context["bulk_sku"] = verified_mode.bulk_sku

        context['currency_data'] = []
        if waffle.switch_is_active('local_currency'):
            if 'edx-price-l10n' not in request.COOKIES:
                currency_data = get_currency_data()
                try:
                    context['currency_data'] = json.dumps(currency_data)
                except TypeError:
                    pass

        language = get_language()
        context['track_links'] = get_verified_track_links(language)

        duration = get_user_course_duration(request.user, course)
        deadline = duration and get_user_course_expiration_date(
            request.user, course)
        if deadline:
            formatted_audit_access_date = strftime_localized_html(
                deadline, 'SHORT_DATE')
            context['audit_access_deadline'] = formatted_audit_access_date
        fbe_is_on = deadline and gated_content

        # Route to correct Track Selection page.
        # REV-2133 TODO Value Prop: remove waffle flag after testing is completed
        # and happy path version is ready to be rolled out to all users.
        if VALUE_PROP_TRACK_SELECTION_FLAG.is_enabled():
            if not error:  # TODO: Remove by executing REV-2355
                if not enterprise_customer_for_request(
                        request):  # TODO: Remove by executing REV-2342
                    if fbe_is_on:
                        return render_to_response("course_modes/fbe.html",
                                                  context)
                    else:
                        return render_to_response("course_modes/unfbe.html",
                                                  context)

        # If error or enterprise_customer, failover to old choose.html page
        return render_to_response("course_modes/choose.html", context)
示例#24
0
 def process_request(self, request):
     """
     Fill the request with Enterprise-related content.
     """
     if 'enterprise_customer' not in request.session and request.user.is_authenticated:
         request.session['enterprise_customer'] = api.enterprise_customer_for_request(request)
示例#25
0
    def test_enterprise_customer_for_request(
        self,
        mock_registry,
        mock_partial,
        mock_enterprise_customer_model,
        mock_get_enterprise_learner_data,
    ):
        def mock_get_enterprise_customer(**kwargs):
            uuid = kwargs.get(
                'enterprise_customer_identity_provider__provider_id')
            if uuid:
                return mock.MagicMock(uuid=uuid, user=self.user)
            raise Exception

        dummy_request = mock.MagicMock(session={}, user=self.user)
        mock_enterprise_customer_model.objects.get.side_effect = mock_get_enterprise_customer
        mock_enterprise_customer_model.DoesNotExist = Exception
        mock_partial.return_value = True
        mock_registry.get_from_pipeline.return_value.provider_id = 'real-ent-uuid'

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user.
        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {'real': 'enterprisecustomer'}, 200)
        enterprise_customer = enterprise_customer_for_request(dummy_request)
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        httpretty.reset()

        # Verify that the method `enterprise_customer_for_request` returns no
        # enterprise customer if the enterprise customer API throws 404.
        del dummy_request.session['enterprise_customer']
        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {'detail': 'Not found.'}, 404)
        enterprise_customer = enterprise_customer_for_request(dummy_request)
        self.assertIsNone(enterprise_customer)

        httpretty.reset()

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user even if
        # the third-party auth pipeline has no `provider_id`.
        mock_registry.get_from_pipeline.return_value.provider_id = None
        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {'real': 'enterprisecustomer'}, 200)
        mock_request = mock.MagicMock(
            GET={'enterprise_customer': 'real-ent-uuid'},
            COOKIES={},
            session={},
            user=self.user)
        enterprise_customer = enterprise_customer_for_request(mock_request)
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user even if
        # the third-party auth pipeline has no `provider_id` but there is
        # enterprise customer UUID in the cookie.
        mock_request = mock.MagicMock(
            GET={},
            COOKIES={
                settings.ENTERPRISE_CUSTOMER_COOKIE_NAME: 'real-ent-uuid'
            },
            session={},
            user=self.user)
        enterprise_customer = enterprise_customer_for_request(mock_request)
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user if
        # data is cached only in the request session
        mock_registry.get_from_pipeline.return_value.provider_id = None
        self.mock_get_enterprise_customer('real-ent-uuid',
                                          {'real': 'enterprisecustomer'}, 200)
        mock_request = mock.MagicMock(
            GET={},
            COOKIES={},
            session={'enterprise_customer': {
                'real': 'enterprisecustomer'
            }},
            user=self.user)
        enterprise_customer = enterprise_customer_for_request(mock_request)
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        # Verify that we can still get enterprise customer from enterprise
        # learner API even if we are unable to get it from preferred sources,
        # e.g. url query parameters, third-party auth pipeline, enterprise
        # cookie, or session.
        mock_get_enterprise_learner_data.return_value = [{
            'enterprise_customer': {
                'uuid': 'real-ent-uuid'
            }
        }]
        mock_request = mock.MagicMock(GET={},
                                      COOKIES={},
                                      session={},
                                      user=self.user,
                                      site=1)
        enterprise_customer = enterprise_customer_for_request(mock_request)
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})
示例#26
0
def _third_party_auth_context(request, redirect_to, tpa_hint=None):
    """Context for third party auth providers and the currently running pipeline.

    Arguments:
        request (HttpRequest): The request, used to determine if a pipeline
            is currently running.
        redirect_to: The URL to send the user to following successful
            authentication.
        tpa_hint (string): An override flag that will return a matching provider
            as long as its configuration has been enabled

    Returns:
        dict

    """
    context = {
        "currentProvider": None,
        "providers": [],
        "secondaryProviders": [],
        "finishAuthUrl": None,
        "errorMessage": None,
    }

    if third_party_auth.is_enabled():
        if not enterprise_customer_for_request(request):
            for enabled in third_party_auth.provider.Registry.displayed_for_login(tpa_hint=tpa_hint):
                info = {
                    "id": enabled.provider_id,
                    "name": enabled.name,
                    "iconClass": enabled.icon_class or None,
                    "iconImage": enabled.icon_image.url if enabled.icon_image else None,
                    "loginUrl": pipeline.get_login_url(
                        enabled.provider_id,
                        pipeline.AUTH_ENTRY_LOGIN,
                        redirect_url=redirect_to,
                    ),
                    "registerUrl": pipeline.get_login_url(
                        enabled.provider_id,
                        pipeline.AUTH_ENTRY_REGISTER,
                        redirect_url=redirect_to,
                    ),
                }
                context["providers" if not enabled.secondary else "secondaryProviders"].append(info)

        running_pipeline = pipeline.get(request)
        if running_pipeline is not None:
            current_provider = third_party_auth.provider.Registry.get_from_pipeline(running_pipeline)

            if current_provider is not None:
                context["currentProvider"] = current_provider.name
                context["finishAuthUrl"] = pipeline.get_complete_url(current_provider.backend_name)

                if current_provider.skip_registration_form:
                    # As a reliable way of "skipping" the registration form, we just submit it automatically
                    context["autoSubmitRegForm"] = True

        # Check for any error messages we may want to display:
        for msg in messages.get_messages(request):
            if msg.extra_tags.split()[0] == "social-auth":
                # msg may or may not be translated. Try translating [again] in case we are able to:
                context['errorMessage'] = _(unicode(msg))  # pylint: disable=translation-of-non-string
                break

    return context
示例#27
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: We check for the existence of login-related cookies in addition to is_authenticated
    #  since Django's SessionAuthentication middleware auto-updates session cookies but not
    #  the other login-related cookies. See ARCH-282.
    if request.user.is_authenticated and are_logged_in_cookies_set(request):
        return redirect(redirect_to)

    # 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 = six.moves.urllib.parse.parse_qs(six.moves.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(u"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_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
示例#28
0
    def get(self, request, course_id, error=None):
        """Displays the course mode choice page.

        Args:
            request (`Request`): The Django Request object.
            course_id (unicode): The slash-separated course key.

        Keyword Args:
            error (unicode): If provided, display this error message
                on the page.

        Returns:
            Response

        """
        course_key = CourseKey.from_string(course_id)

        # Check whether the user has access to this course
        # based on country access rules.
        embargo_redirect = embargo_api.redirect_if_blocked(
            course_key,
            user=request.user,
            ip_address=get_ip(request),
            url=request.path
        )
        if embargo_redirect:
            return redirect(embargo_redirect)

        enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(request.user, course_key)

        increment('track-selection.{}.{}'.format(enrollment_mode, 'active' if is_active else 'inactive'))
        increment('track-selection.views')

        if enrollment_mode is None:
            LOG.info('Rendering track selection for unenrolled user, referred by %s', request.META.get('HTTP_REFERER'))

        modes = CourseMode.modes_for_course_dict(course_key)
        ecommerce_service = EcommerceService()

        # We assume that, if 'professional' is one of the modes, it should be the *only* mode.
        # If there are both modes, default to non-id-professional.
        has_enrolled_professional = (CourseMode.is_professional_slug(enrollment_mode) and is_active)
        if CourseMode.has_professional_mode(modes) and not has_enrolled_professional:
            purchase_workflow = request.GET.get("purchase_workflow", "single")
            verify_url = reverse('verify_student_start_flow', kwargs={'course_id': six.text_type(course_key)})
            redirect_url = "{url}?purchase_workflow={workflow}".format(url=verify_url, workflow=purchase_workflow)
            if ecommerce_service.is_enabled(request.user):
                professional_mode = modes.get(CourseMode.NO_ID_PROFESSIONAL_MODE) or modes.get(CourseMode.PROFESSIONAL)
                if purchase_workflow == "single" and professional_mode.sku:
                    redirect_url = ecommerce_service.get_checkout_page_url(professional_mode.sku)
                if purchase_workflow == "bulk" and professional_mode.bulk_sku:
                    redirect_url = ecommerce_service.get_checkout_page_url(professional_mode.bulk_sku)
            return redirect(redirect_url)

        course = modulestore().get_course(course_key)

        # If there isn't a verified mode available, then there's nothing
        # to do on this page.  Send the user to the dashboard.
        if not CourseMode.has_verified_mode(modes):
            return redirect(reverse('dashboard'))

        # If a user has already paid, redirect them to the dashboard.
        if is_active and (enrollment_mode in CourseMode.VERIFIED_MODES + [CourseMode.NO_ID_PROFESSIONAL_MODE]):
            # If the course has started redirect to course home instead
            if course.has_started():
                return redirect(reverse('openedx.course_experience.course_home', kwargs={'course_id': course_key}))
            return redirect(reverse('dashboard'))

        donation_for_course = request.session.get("donation_for_course", {})
        chosen_price = donation_for_course.get(six.text_type(course_key), None)

        if CourseEnrollment.is_enrollment_closed(request.user, course):
            locale = to_locale(get_language())
            enrollment_end_date = format_datetime(course.enrollment_end, 'short', locale=locale)
            params = six.moves.urllib.parse.urlencode({'course_closed': enrollment_end_date})
            return redirect('{0}?{1}'.format(reverse('dashboard'), params))

        # When a credit mode is available, students will be given the option
        # to upgrade from a verified mode to a credit mode at the end of the course.
        # This allows students who have completed photo verification to be eligible
        # for university credit.
        # Since credit isn't one of the selectable options on the track selection page,
        # we need to check *all* available course modes in order to determine whether
        # a credit mode is available.  If so, then we show slightly different messaging
        # for the verified track.
        has_credit_upsell = any(
            CourseMode.is_credit_mode(mode) for mode
            in CourseMode.modes_for_course(course_key, only_selectable=False)
        )
        course_id = text_type(course_key)

        context = {
            "course_modes_choose_url": reverse(
                "course_modes_choose",
                kwargs={'course_id': course_id}
            ),
            "modes": modes,
            "has_credit_upsell": has_credit_upsell,
            "course_name": course.display_name_with_default,
            "course_org": course.display_org_with_default,
            "course_num": course.display_number_with_default,
            "chosen_price": chosen_price,
            "error": error,
            "responsive": True,
            "nav_hidden": True,
            "content_gating_enabled": ContentTypeGatingConfig.enabled_for_enrollment(
                user=request.user,
                course_key=course_key
            ),
            "course_duration_limit_enabled": CourseDurationLimitConfig.enabled_for_enrollment(
                user=request.user,
                course_key=course_key
            ),
        }
        context.update(
            get_experiment_user_metadata_context(
                course,
                request.user,
            )
        )

        title_content = ''
        if enrollment_mode:
            title_content = _("Congratulations!  You are now enrolled in {course_name}").format(
                course_name=course.display_name_with_default
            )

        context["title_content"] = title_content

        if "verified" in modes:
            verified_mode = modes["verified"]
            context["suggested_prices"] = [
                decimal.Decimal(x.strip())
                for x in verified_mode.suggested_prices.split(",")
                if x.strip()
            ]
            price_before_discount = verified_mode.min_price
            course_price = price_before_discount
            enterprise_customer = enterprise_customer_for_request(request)
            if enterprise_customer and verified_mode.sku:
                course_price = get_course_final_price(request.user, verified_mode.sku, price_before_discount)

            context["currency"] = verified_mode.currency.upper()
            context["currency_symbol"] = get_currency_symbol(verified_mode.currency.upper())
            context["min_price"] = course_price
            context["verified_name"] = verified_mode.name
            context["verified_description"] = verified_mode.description
            # if course_price is equal to price_before_discount then user doesn't entitle to any discount.
            if course_price != price_before_discount:
                context["price_before_discount"] = price_before_discount

            if verified_mode.sku:
                context["use_ecommerce_payment_flow"] = ecommerce_service.is_enabled(request.user)
                context["ecommerce_payment_page"] = ecommerce_service.payment_page_url()
                context["sku"] = verified_mode.sku
                context["bulk_sku"] = verified_mode.bulk_sku

        context['currency_data'] = []
        if waffle.switch_is_active('local_currency'):
            if 'edx-price-l10n' not in request.COOKIES:
                currency_data = get_currency_data()
                try:
                    context['currency_data'] = json.dumps(currency_data)
                except TypeError:
                    pass
        return render_to_response("course_modes/choose.html", context)
示例#29
0
    def test_enterprise_customer_for_request(
            self,
            mock_registry,
            mock_partial,
            mock_enterprise_customer_model,
            mock_get_enterprise_learner_data,
    ):
        def mock_get_enterprise_customer(**kwargs):
            uuid = kwargs.get('enterprise_customer_identity_provider__provider_id')
            if uuid:
                return mock.MagicMock(uuid=uuid, user=self.user)
            raise Exception

        dummy_request = mock.MagicMock(session={}, user=self.user)
        mock_enterprise_customer_model.objects.get.side_effect = mock_get_enterprise_customer
        mock_enterprise_customer_model.DoesNotExist = Exception
        mock_partial.return_value = True
        mock_registry.get_from_pipeline.return_value.provider_id = 'real-ent-uuid'

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user.
        self.mock_get_enterprise_customer('real-ent-uuid', {'real': 'enterprisecustomer'}, 200)
        enterprise_customer = enterprise_customer_for_request(dummy_request)
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        httpretty.reset()

        # Verify that the method `enterprise_customer_for_request` returns no
        # enterprise customer if the enterprise customer API throws 404.
        self.mock_get_enterprise_customer('real-ent-uuid', {'detail': 'Not found.'}, 404)
        enterprise_customer = enterprise_customer_for_request(dummy_request)
        self.assertIsNone(enterprise_customer)

        httpretty.reset()

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user even if
        # the third-party auth pipeline has no `provider_id`.
        mock_registry.get_from_pipeline.return_value.provider_id = None
        self.mock_get_enterprise_customer('real-ent-uuid', {'real': 'enterprisecustomer'}, 200)
        enterprise_customer = enterprise_customer_for_request(
            mock.MagicMock(GET={'enterprise_customer': 'real-ent-uuid'}, user=self.user)
        )
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        # Verify that the method `enterprise_customer_for_request` returns
        # expected enterprise customer against the requesting user even if
        # the third-party auth pipeline has no `provider_id` but there is
        # enterprise customer UUID in the cookie.
        enterprise_customer = enterprise_customer_for_request(
            mock.MagicMock(GET={}, COOKIES={settings.ENTERPRISE_CUSTOMER_COOKIE_NAME: 'real-ent-uuid'}, user=self.user)
        )
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})

        # Verify that we can still get enterprise customer from enterprise
        # learner API even if we are unable to get it from preferred sources,
        # e.g. url query parameters, third-party auth pipeline, enterprise
        # cookie.
        mock_get_enterprise_learner_data.return_value = [{'enterprise_customer': {'uuid': 'real-ent-uuid'}}]
        enterprise_customer = enterprise_customer_for_request(
            mock.MagicMock(GET={}, COOKIES={}, user=self.user, site=1)
        )
        self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'})
示例#30
0
def account_settings_context(request):
    """ Context for the account settings page.

    Args:
        request: The request object.

    Returns:
        dict

    """
    user = request.user

    year_of_birth_options = [(six.text_type(year), six.text_type(year))
                             for year in UserProfile.VALID_YEARS]
    try:
        user_orders = get_user_orders(user)
    except:  # pylint: disable=bare-except
        log.exception('Error fetching order history from Otto.')
        # Return empty order list as account settings page expect a list and
        # it will be broken if exception raised
        user_orders = []

    beta_language = {}
    dark_lang_config = DarkLangConfig.current()
    if dark_lang_config.enable_beta_languages:
        user_preferences = get_user_preferences(user)
        pref_language = user_preferences.get('pref-lang')
        if pref_language in dark_lang_config.beta_languages_list:
            beta_language['code'] = pref_language
            beta_language['name'] = settings.LANGUAGE_DICT.get(pref_language)

    context = {
        'auth': {},
        'duplicate_provider':
        None,
        'nav_hidden':
        True,
        'fields': {
            'country': {
                'options': list(countries),
            },
            'gender': {
                'options': [(choice[0], _(choice[1]))
                            for choice in UserProfile.GENDER_CHOICES],
            },
            'language': {
                'options': released_languages(),
            },
            'level_of_education': {
                'options':
                [(choice[0], _(choice[1]))
                 for choice in UserProfile.LEVEL_OF_EDUCATION_CHOICES],
            },
            'password': {
                'url': reverse('password_reset'),
            },
            'year_of_birth': {
                'options': year_of_birth_options,
            },
            'preferred_language': {
                'options': all_languages(),
            },
            'time_zone': {
                'options': TIME_ZONE_CHOICES,
            }
        },
        'platform_name':
        configuration_helpers.get_value('PLATFORM_NAME',
                                        settings.PLATFORM_NAME),
        'password_reset_support_link':
        configuration_helpers.get_value('PASSWORD_RESET_SUPPORT_LINK',
                                        settings.PASSWORD_RESET_SUPPORT_LINK)
        or settings.SUPPORT_SITE_LINK,
        'user_accounts_api_url':
        reverse("accounts_api", kwargs={'username': user.username}),
        'user_preferences_api_url':
        reverse('preferences_api', kwargs={'username': user.username}),
        'disable_courseware_js':
        True,
        'show_program_listing':
        ProgramsApiConfig.is_enabled(),
        'show_dashboard_tabs':
        True,
        'order_history':
        user_orders,
        'disable_order_history_tab':
        should_redirect_to_order_history_microfrontend(),
        'enable_account_deletion':
        configuration_helpers.get_value(
            'ENABLE_ACCOUNT_DELETION',
            settings.FEATURES.get('ENABLE_ACCOUNT_DELETION', False)),
        'extended_profile_fields':
        _get_extended_profile_fields(),
        'beta_language':
        beta_language,
    }

    enterprise_customer = enterprise_customer_for_request(request)
    update_account_settings_context_for_enterprise(context,
                                                   enterprise_customer, user)

    if third_party_auth.is_enabled():
        # If the account on the third party provider is already connected with another edX account,
        # we display a message to the user.
        context['duplicate_provider'] = pipeline.get_duplicate_provider(
            messages.get_messages(request))

        auth_states = pipeline.get_provider_user_states(user)

        context['auth']['providers'] = [
            {
                'id':
                state.provider.provider_id,
                'name':
                state.provider.name,  # The name of the provider e.g. Facebook
                'connected':
                state.
                has_account,  # Whether the user's edX account is connected with the provider.
                # If the user is not connected, they should be directed to this page to authenticate
                # with the particular provider, as long as the provider supports initiating a login.
                'connect_url':
                pipeline.get_login_url(
                    state.provider.provider_id,
                    pipeline.AUTH_ENTRY_ACCOUNT_SETTINGS,
                    # The url the user should be directed to after the auth process has completed.
                    redirect_url=reverse('account_settings'),
                ),
                'accepts_logins':
                state.provider.accepts_logins,
                # If the user is connected, sending a POST request to this url removes the connection
                # information for this provider from their edX account.
                'disconnect_url':
                pipeline.get_disconnect_url(state.provider.provider_id,
                                            state.association_id),
                # We only want to include providers if they are either currently available to be logged
                # in with, or if the user is already authenticated with them.
            } for state in auth_states
            if state.provider.display_for_login or state.has_account
        ]

    return context
示例#31
0
def _third_party_auth_context(request, redirect_to, tpa_hint=None):
    """Context for third party auth providers and the currently running pipeline.

    Arguments:
        request (HttpRequest): The request, used to determine if a pipeline
            is currently running.
        redirect_to: The URL to send the user to following successful
            authentication.
        tpa_hint (string): An override flag that will return a matching provider
            as long as its configuration has been enabled

    Returns:
        dict

    """
    context = {
        "currentProvider": None,
        "providers": [],
        "secondaryProviders": [],
        "finishAuthUrl": None,
        "errorMessage": None,
        "registerFormSubmitButtonText": _("Create Account"),
    }

    if third_party_auth.is_enabled():
        enterprise_customer = enterprise_customer_for_request(request)
        if not enterprise_customer:
            for enabled in third_party_auth.provider.Registry.displayed_for_login(
                    tpa_hint=tpa_hint):
                info = {
                    "id":
                    enabled.provider_id,
                    "name":
                    enabled.name,
                    "iconClass":
                    enabled.icon_class or None,
                    "iconImage":
                    enabled.icon_image.url if enabled.icon_image else None,
                    "loginUrl":
                    pipeline.get_login_url(
                        enabled.provider_id,
                        pipeline.AUTH_ENTRY_LOGIN,
                        redirect_url=redirect_to,
                    ),
                    "registerUrl":
                    pipeline.get_login_url(
                        enabled.provider_id,
                        pipeline.AUTH_ENTRY_REGISTER,
                        redirect_url=redirect_to,
                    ),
                }
                context["providers" if not enabled.
                        secondary else "secondaryProviders"].append(info)

        running_pipeline = pipeline.get(request)
        if running_pipeline is not None:
            current_provider = third_party_auth.provider.Registry.get_from_pipeline(
                running_pipeline)

            if current_provider is not None:
                context["currentProvider"] = current_provider.name
                context["finishAuthUrl"] = pipeline.get_complete_url(
                    current_provider.backend_name)

                if current_provider.skip_registration_form:
                    # For enterprise (and later for everyone), we need to get explicit consent to the
                    # Terms of service instead of auto submitting the registration form outright.
                    if not enterprise_customer:
                        # As a reliable way of "skipping" the registration form, we just submit it automatically
                        context["autoSubmitRegForm"] = True
                    else:
                        context["autoRegisterWelcomeMessage"] = (
                            'Thank you for joining {}. '
                            'Just a couple steps before you start learning!'
                        ).format(
                            configuration_helpers.get_value(
                                'PLATFORM_NAME', settings.PLATFORM_NAME))
                        context["registerFormSubmitButtonText"] = _("Continue")

        # Check for any error messages we may want to display:
        for msg in messages.get_messages(request):
            if msg.extra_tags.split()[0] == "social-auth":
                # msg may or may not be translated. Try translating [again] in case we are able to:
                context['errorMessage'] = _(unicode(msg))  # pylint: disable=translation-of-non-string
                break

    return context
示例#32
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
    if request.user.is_authenticated:
        return redirect(redirect_to)

    # 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:
        try:
            next_args = urlparse.parse_qs(urlparse.urlparse(redirect_to).query)
            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.error("Unknown tpa_hint provider: %s", ex)

    # If this is a themed site, revert to the old login/registration pages.
    # We need to do this for now to support existing themes.
    # Themed sites can use the new logistration page by setting
    # 'ENABLE_COMBINED_LOGIN_REGISTRATION' in their
    # configuration settings.
    if is_request_in_themed_site() and not configuration_helpers.get_value('ENABLE_COMBINED_LOGIN_REGISTRATION', False):
        if initial_mode == "login":
            return old_login_view(request)
        elif initial_mode == "register":
            return old_register_view(request)

    # Allow external auth to intercept and handle the request
    ext_auth_response = _external_auth_intercept(request, initial_mode)
    if ext_auth_response is not None:
        return ext_auth_response

    # 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
    ]

    # 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,

            # 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))
        },
        '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']
        ),
    }

    enterprise_customer = enterprise_customer_for_request(request)
    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