예제 #1
0
 def test_get_ec_for_request(self):
     """
     Test that get_ec_for_request works.
     """
     request = mock.MagicMock(session={'partial_pipeline': 'pipeline_key'})
     with mock.patch('enterprise.tpa_pipeline.Registry') as fake_registry:
         fake_provider = mock.MagicMock()
         fake_provider.provider_id = 'provider_slug'
         fake_registry.get_from_pipeline.return_value = fake_provider
         assert get_enterprise_customer_for_request(request) == self.customer
     with raises(NotConnectedToEdX) as excinfo:
         get_enterprise_customer_for_request(request)
     expected_msg = "This package must be installed in an EdX environment to look up third-party auth providers."
     assert str(excinfo.value) == expected_msg
예제 #2
0
def add_data_sharing_consent_field(request, form_desc):
    """
    Adds a checkbox field to be selected if the user consents to share data with
    the EnterpriseCustomer attached to the SSO provider with which they're authenticating.
    """
    enterprise_customer = get_enterprise_customer_for_request(request)
    required = data_sharing_consent_required_at_login(request)

    if not data_sharing_consent_requested(request):
        return

    label = _(
        "I agree to allow {platform_name} to share data about my enrollment, "
        "completion and performance in all {platform_name} courses and programs "
        "where my enrollment is sponsored by {ec_name}."
    ).format(
        platform_name=configuration_helpers.get_value("PLATFORM_NAME", settings.PLATFORM_NAME),
        ec_name=enterprise_customer.name
    )

    error_msg = _(
        "To link your account with {ec_name}, you are required to consent to data sharing."
    ).format(
        ec_name=enterprise_customer.name
    )

    form_desc.add_field(
        "data_sharing_consent",
        label=label,
        field_type="checkbox",
        default=False,
        required=required,
        error_messages={"required": error_msg},
    )
예제 #3
0
    def get(self, request):
        """
        Render a form to collect user input about data sharing consent.
        """
        # Verify that all necessary resources are present
        verify_edx_resources()
        # Get the OpenEdX platform name
        platform_name = configuration_helpers.get_value(
            "PLATFORM_NAME", settings.PLATFORM_NAME)

        # Get the EnterpriseCustomer for the request; raise an error if there isn't one.
        customer = get_enterprise_customer_for_request(request)
        if customer is None:
            raise Http404

        # Quarantine the user to this module.
        self.quarantine(request)

        required = customer.enforces_data_sharing_consent(
            EnterpriseCustomer.AT_LOGIN)

        data = {
            'platform_name': platform_name,
            'sso_provider': customer.name,
            'data_sharing_consent': 'required' if required else 'optional',
            "messages": {
                "warning": self.get_warning(customer.name, platform_name,
                                            required),
                "note": self.get_note(customer.name, required),
            },
        }
        return render_to_response('grant_data_sharing_permissions.html',
                                  data,
                                  request=request)
def add_data_sharing_consent_field(request, form_desc):
    """
    Adds a checkbox field to be selected if the user consents to share data with
    the EnterpriseCustomer attached to the SSO provider with which they're authenticating.
    """
    enterprise_customer = get_enterprise_customer_for_request(request)
    required = data_sharing_consent_required_at_login(request)

    if not data_sharing_consent_requested(request):
        return

    label = _(
        "I agree to allow {platform_name} to share data about my enrollment, "
        "completion and performance in all {platform_name} courses and programs "
        "where my enrollment is sponsored by {ec_name}."
    ).format(
        platform_name=configuration_helpers.get_value("PLATFORM_NAME", settings.PLATFORM_NAME),
        ec_name=enterprise_customer.name
    )

    error_msg = _(
        "To link your account with {ec_name}, you are required to consent to data sharing."
    ).format(
        ec_name=enterprise_customer.name
    )

    form_desc.add_field(
        "data_sharing_consent",
        label=label,
        field_type="checkbox",
        default=False,
        required=required,
        error_messages={"required": error_msg},
    )
예제 #5
0
def enterprise_customer_for_request(request, tpa_hint=None):
    """
    Check all the context clues of the request to determine if
    the request being made is tied to a particular EnterpriseCustomer.
    """
    if not enterprise_enabled():
        return None

    ec = get_enterprise_customer_for_request(request)

    if not ec and tpa_hint:
        try:
            ec = EnterpriseCustomer.objects.get(
                enterprise_customer_identity_provider__provider_id=tpa_hint)
        except EnterpriseCustomer.DoesNotExist:
            pass

    ec_uuid = request.GET.get('enterprise_customer') or request.COOKIES.get(
        settings.ENTERPRISE_CUSTOMER_COOKIE_NAME)
    if not ec and ec_uuid:
        try:
            ec = EnterpriseCustomer.objects.get(uuid=ec_uuid)
        except (EnterpriseCustomer.DoesNotExist, ValueError):
            ec = None

    return ec
예제 #6
0
    def get_account_consent(self, request):
        """
        Render a form to collect consent for account-wide data sharing.

        This method is called when no course ID is passed as a URL parameter; a form will be
        rendered with messaging around the concept of granting consent for the entire platform.
        """
        # Get the OpenEdX platform name
        platform_name = configuration_helpers.get_value(
            "PLATFORM_NAME", settings.PLATFORM_NAME)

        # Get the EnterpriseCustomer for the request; raise an error if there isn't one.
        customer = get_enterprise_customer_for_request(request)
        if customer is None:
            raise Http404

        # Quarantine the user to this module.
        self.quarantine(request)

        failure_url = request.GET.get('failure_url')

        context_data = self.get_default_context(customer, platform_name)

        account_specific_context = {
            'consent_request_prompt':
            _('To log in using this SSO identity provider and access special course offers, you must first '
              'consent to share your learning achievements with {enterprise_customer_name}.'
              ).format(enterprise_customer_name=customer.name),
            'confirmation_alert_prompt':
            _('In order to sign in and access special offers, you must consent to share your '
              'course data with {enterprise_customer_name}.').format(
                  enterprise_customer_name=customer.name),
            'page_language':
            get_language_from_request(request),
            'platform_name':
            platform_name,
            'enterprise_customer_name':
            customer.name,
            "course_id":
            None,
            "course_specific":
            False,
            'enrollment_deferred':
            False,
            'failure_url':
            failure_url,
            'requested_permissions': [
                _('your enrollment in all sponsored courses'),
                _('your learning progress'),
                _('course completion'),
            ]
        }

        context_data.update(account_specific_context)

        return render_to_response('grant_data_sharing_permissions.html',
                                  context_data,
                                  request=request)
예제 #7
0
 def test_get_ec_for_request(self, fake_pipeline):
     """
     Test that get_ec_for_request works.
     """
     request = mock.MagicMock()
     fake_provider = mock.MagicMock()
     fake_provider.provider_id = 'provider_slug'
     fake_pipeline.return_value = {
         'kwargs': {
             'access_token': 'dummy'
         },
         'backend': 'fake_backend'
     }
     with mock.patch('enterprise.tpa_pipeline.Registry') as fake_registry:
         fake_registry.get_from_pipeline.return_value = fake_provider
         assert get_enterprise_customer_for_request(
             request) == self.customer
     with raises(NotConnectedToOpenEdX) as excinfo:
         get_enterprise_customer_for_request(request)
     self.assertIsNotNone(excinfo.value)
예제 #8
0
    def post_account_consent(self, request, consent_provided):
        """
        Interpret the account-wide form above, and save it to a UserDataSharingConsentAudit object for later retrieval.
        """
        self.lift_quarantine(request)

        # Load the linked EnterpriseCustomer for this request.
        customer = get_enterprise_customer_for_request(request)
        if customer is None:
            # If we can't get an EnterpriseCustomer from the pipeline, then we don't really
            # have enough state to do anything meaningful. Just send the user to the login
            # screen; if they want to sign in with an Enterprise-linked SSO, they can do
            # so, and the pipeline will get them back here if they need to be.
            return redirect('signin_user')

        # Attempt to retrieve a user being manipulated by the third-party auth
        # pipeline. Return a 404 if no such user exists.
        social_auth = get_real_social_auth_object(request)
        user = getattr(social_auth, 'user', None)
        if user is None:
            raise Http404

        if not consent_provided and active_provider_enforces_data_sharing(
                request, EnterpriseCustomer.AT_LOGIN):
            # Flush the session to avoid the possibility of accidental login and to abort the pipeline.
            # pipeline is flushed only if data sharing is enforced, in other cases let the user to login.
            request.session.flush()
            failure_url = request.POST.get('failure_url') or reverse(
                'dashboard')
            return redirect(failure_url)

        ec_user, __ = EnterpriseCustomerUser.objects.get_or_create(
            user_id=user.id,
            enterprise_customer=customer,
        )

        UserDataSharingConsentAudit.objects.update_or_create(
            user=ec_user,
            defaults={
                'state':
                (UserDataSharingConsentAudit.ENABLED
                 if consent_provided else UserDataSharingConsentAudit.DISABLED)
            })

        # Resume auth pipeline
        backend_name = request.session.get('partial_pipeline',
                                           {}).get('backend')
        return redirect(get_complete_url(backend_name))
예제 #9
0
    def post_account_consent(self, request, consent_provided):
        """
        Interpret the account-wide form above, and save it to a UserDataSharingConsentAudit object for later retrieval.
        """
        self.lift_quarantine(request)

        # Load the linked EnterpriseCustomer for this request. Return a 404 if no such EnterpriseCustomer exists
        customer = get_enterprise_customer_for_request(request)
        if customer is None:
            raise Http404

        # Attempt to retrieve a user being manipulated by the third-party auth
        # pipeline. Return a 404 if no such user exists.
        social_auth = get_real_social_auth_object(request)
        user = getattr(social_auth, 'user', None)
        if user is None:
            raise Http404

        if not consent_provided and active_provider_enforces_data_sharing(
                request, EnterpriseCustomer.AT_LOGIN):
            # Flush the session to avoid the possibility of accidental login and to abort the pipeline.
            # pipeline is flushed only if data sharing is enforced, in other cases let the user to login.
            request.session.flush()
            return redirect(reverse('dashboard'))

        ec_user, __ = EnterpriseCustomerUser.objects.get_or_create(
            user_id=user.id,
            enterprise_customer=customer,
        )

        UserDataSharingConsentAudit.objects.update_or_create(
            user=ec_user,
            defaults={
                'state':
                (UserDataSharingConsentAudit.ENABLED
                 if consent_provided else UserDataSharingConsentAudit.DISABLED)
            })

        # Resume auth pipeline
        backend_name = request.session.get('partial_pipeline',
                                           {}).get('backend')
        return redirect(get_complete_url(backend_name))
예제 #10
0
    def get_account_consent(self, request):
        """
        Render a form to collect consent for account-wide data sharing.

        This method is called when no course ID is passed as a URL parameter; a form will be
        rendered with messaging around the concept of granting consent for the entire platform.
        """
        # Get the OpenEdX platform name
        platform_name = configuration_helpers.get_value(
            "PLATFORM_NAME", settings.PLATFORM_NAME)

        # Get the EnterpriseCustomer for the request; raise an error if there isn't one.
        customer = get_enterprise_customer_for_request(request)
        if customer is None:
            raise Http404

        # Quarantine the user to this module.
        self.quarantine(request)

        required = customer.enforces_data_sharing_consent(
            EnterpriseCustomer.AT_LOGIN)

        data = {
            'platform_name': platform_name,
            'enterprise_customer_name': customer.name,
            'data_sharing_consent': 'required' if required else 'optional',
            "messages": {
                "warning": self.get_warning(customer.name, platform_name,
                                            required),
                "note": self.get_note(customer.name, required),
            },
            "course_id": None,
            "course_specific": False,
            'enrollment_deferred': False,
        }
        return render_to_response('grant_data_sharing_permissions.html',
                                  data,
                                  request=request)
예제 #11
0
    def post(self, request):
        """
        Process the above form.
        """
        # Verify that all necessary resources are present
        verify_edx_resources()
        self.lift_quarantine(request)
        customer = get_enterprise_customer_for_request(request)
        if customer is None:
            raise Http404
        consent_provided = request.POST.get('data_sharing_consent', False)
        # If the checkbox is unchecked, no value will be sent
        user = get_real_social_auth_object(request).user
        ec_user, __ = EnterpriseCustomerUser.objects.get_or_create(
            user_id=user.id,
            enterprise_customer=customer,
        )

        UserDataSharingConsentAudit.objects.update_or_create(
            user=ec_user,
            defaults={
                'state':
                (UserDataSharingConsentAudit.ENABLED
                 if consent_provided else UserDataSharingConsentAudit.DISABLED)
            })
        if not consent_provided:
            # Flush the session to avoid the possibility of accidental login and to abort the pipeline.
            # pipeline is flushed only if data sharing is enforced, in other cases let the user to login.
            if active_provider_enforces_data_sharing(
                    request, EnterpriseCustomer.AT_LOGIN):
                request.session.flush()
                return redirect(reverse('dashboard'))

        # Resume auth pipeline
        backend_name = request.session.get('partial_pipeline',
                                           {}).get('backend')
        return redirect(get_complete_url(backend_name))
예제 #12
0
def enterprise_customer_for_request(request, tpa_hint=None):
    """
    Check all the context clues of the request to determine if
    the request being made is tied to a particular EnterpriseCustomer.
    """
    if not enterprise_enabled():
        return None

    ec = get_enterprise_customer_for_request(request)

    if not ec and tpa_hint:
        try:
            ec = EnterpriseCustomer.objects.get(enterprise_customer_identity_provider__provider_id=tpa_hint)
        except EnterpriseCustomer.DoesNotExist:
            pass

    ec_uuid = request.GET.get('enterprise_customer') or request.COOKIES.get(settings.ENTERPRISE_CUSTOMER_COOKIE_NAME)
    if not ec and ec_uuid:
        try:
            ec = EnterpriseCustomer.objects.get(uuid=ec_uuid)
        except (EnterpriseCustomer.DoesNotExist, ValueError):
            ec = None

    return ec
예제 #13
0
    def post_account_consent(self, request, consent_provided):
        """
        Interpret the account-wide form above, and save it to a UserDataSharingConsentAudit object for later retrieval.
        """
        self.lift_quarantine(request)

        # Load the linked EnterpriseCustomer for this request.
        customer = get_enterprise_customer_for_request(request)
        if customer is None:
            # If we can't get an EnterpriseCustomer from the pipeline, then we don't really
            # have enough state to do anything meaningful. Just send the user to the login
            # screen; if they want to sign in with an Enterprise-linked SSO, they can do
            # so, and the pipeline will get them back here if they need to be.
            return redirect('signin_user')

        # Attempt to retrieve a user being manipulated by the third-party auth
        # pipeline. Return a 404 if no such user exists.
        social_auth = get_real_social_auth_object(request)
        user = getattr(social_auth, 'user', None)
        if user is None:
            raise Http404

        if not consent_provided and active_provider_enforces_data_sharing(
                request, EnterpriseCustomer.AT_LOGIN):
            # Flush the session to avoid the possibility of accidental login and to abort the pipeline.
            # pipeline is flushed only if data sharing is enforced, in other cases let the user to login.
            request.session.flush()
            failure_url = request.POST.get('failure_url') or reverse(
                'dashboard')
            return redirect(failure_url)

        enterprise_customer_user, __ = EnterpriseCustomerUser.objects.get_or_create(
            user_id=user.id,
            enterprise_customer=customer,
        )

        platform_name = configuration_helpers.get_value(
            'PLATFORM_NAME', settings.PLATFORM_NAME)
        messages.success(
            request,
            _('{span_start}Account created{span_end} Thank you for creating an account with {platform_name}.'
              ).format(
                  platform_name=platform_name,
                  span_start='<span>',
                  span_end='</span>',
              ))
        if not user.is_active:
            messages.info(
                request,
                _('{span_start}Activate your account{span_end} Check your inbox for an activation email. '
                  'You will not be able to log back into your account until you have activated it.'
                  ).format(span_start='<span>', span_end='</span>'))

        UserDataSharingConsentAudit.objects.update_or_create(
            user=enterprise_customer_user,
            defaults={
                'state':
                (UserDataSharingConsentAudit.ENABLED
                 if consent_provided else UserDataSharingConsentAudit.DISABLED)
            })

        # Resume auth pipeline
        backend_name = get_partial_pipeline(request).get('backend')
        return redirect(get_complete_url(backend_name))
예제 #14
0
    def get_account_consent(self, request):
        """
        Render a form to collect consent for account-wide data sharing.

        This method is called when no course ID is passed as a URL parameter; a form will be
        rendered with messaging around the concept of granting consent for the entire platform.
        """
        # Get the OpenEdX platform name
        platform_name = configuration_helpers.get_value(
            "PLATFORM_NAME", settings.PLATFORM_NAME)

        # Get the EnterpriseCustomer for the request.
        customer = get_enterprise_customer_for_request(request)
        if customer is None:
            # If we can't get an EnterpriseCustomer from the pipeline, then we don't really
            # have enough state to do anything meaningful. Just send the user to the login
            # screen; if they want to sign in with an Enterprise-linked SSO, they can do
            # so, and the pipeline will get them back here if they need to be.
            return redirect('signin_user')

        # Quarantine the user to this module.
        self.quarantine(request)

        failure_url = request.GET.get('failure_url')

        context_data = self.get_default_context(customer, platform_name)

        account_specific_context = {
            'consent_request_prompt':
            CONSENT_REQUEST_PROMPT.format(  # pylint: disable=no-member
                enterprise_customer_name=customer.name),
            'confirmation_alert_prompt':
            CONFIRMATION_ALERT_PROMPT.format(  # pylint: disable=no-member
                enterprise_customer_name=customer.name),
            'confirmation_alert_prompt_warning':
            CONFIRMATION_ALERT_PROMPT_WARNING.format(  # pylint: disable=no-member
                enterprise_customer_name=customer.name, ),
            'LANGUAGE_CODE':
            get_language_from_request(request),
            'platform_name':
            platform_name,
            'enterprise_customer_name':
            customer.name,
            "course_id":
            None,
            "course_specific":
            False,
            'enrollment_deferred':
            False,
            'failure_url':
            failure_url,
            'requested_permissions': [
                _('your enrollment in all sponsored courses'),
                _('your learning progress'),
                _('course completion'),
            ],
            'enterprise_customer':
            customer,
            'enterprise_welcome_text':
            self.enterprise_welcome_text.format(
                enterprise_customer_name=customer.name,
                platform_name=platform_name,
                strong_start='<strong>',
                strong_end='</strong>',
            ),
        }

        context_data.update(account_specific_context)

        return render(request,
                      'enterprise/grant_data_sharing_permissions.html',
                      context=context_data)