コード例 #1
0
def confirm_email_change(request, key):
    """
    User requested a new e-mail. This is called when the activation
    link is clicked. We confirm with the old e-mail, and update
    """
    with transaction.atomic():
        try:
            pec = PendingEmailChange.objects.get(activation_key=key)
        except PendingEmailChange.DoesNotExist:
            response = render_to_response("invalid_email_key.html", {})
            transaction.set_rollback(True)
            return response

        user = pec.user
        address_context = {
            'old_email': user.email,
            'new_email': pec.new_email
        }

        if len(User.objects.filter(email=pec.new_email)) != 0:
            response = render_to_response("email_exists.html", {})
            transaction.set_rollback(True)
            return response

        use_https = request.is_secure()
        if settings.FEATURES['ENABLE_MKTG_SITE']:
            contact_link = marketing_link('CONTACT')
        else:
            contact_link = '{protocol}://{site}{link}'.format(
                protocol='https' if use_https else 'http',
                site=configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME),
                link=reverse('contact'),
            )

        site = Site.objects.get_current()
        message_context = get_base_template_context(site)
        message_context.update({
            'old_email': user.email,
            'new_email': pec.new_email,
            'contact_link': contact_link,
            'from_address': configuration_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL),
        })

        msg = EmailChangeConfirmation().personalize(
            recipient=Recipient(user.id, user.email),
            language=preferences_api.get_user_preference(user, LANGUAGE_KEY),
            user_context=message_context,
        )

        u_prof = UserProfile.objects.get(user=user)
        meta = u_prof.get_meta()
        if 'old_emails' not in meta:
            meta['old_emails'] = []
        meta['old_emails'].append([user.email, datetime.datetime.now(UTC).isoformat()])
        u_prof.set_meta(meta)
        u_prof.save()
        # Send it to the old email...
        try:
            ace.send(msg)
        except Exception:  # pylint: disable=broad-except
            log.warning('Unable to send confirmation email to old address', exc_info=True)
            response = render_to_response("email_change_failed.html", {'email': user.email})
            transaction.set_rollback(True)
            return response

        user.email = pec.new_email
        user.save()
        pec.delete()
        # And send it to the new email...
        msg.recipient = Recipient(user.id, pec.new_email)
        try:
            ace.send(msg)
        except Exception:  # pylint: disable=broad-except
            log.warning('Unable to send confirmation email to new address', exc_info=True)
            response = render_to_response("email_change_failed.html", {'email': pec.new_email})
            transaction.set_rollback(True)
            return response

        response = render_to_response("email_change_successful.html", address_context)
        return response
コード例 #2
0
ファイル: test_tasks.py プロジェクト: perthcpe23/edx-platform
    def test_send_discussion_email_notification(self, user_subscribed):
        if user_subscribed:
            non_matching_id = 'not-a-match'
            # with per_page left with a default value of 1, this ensures
            # that we test a multiple page result when calling
            # comment_client.User.subscribed_threads()
            subscribed_thread_ids = [non_matching_id, self.discussion_id]
        else:
            subscribed_thread_ids = []

        self.mock_request.side_effect = make_mock_responder(
            subscribed_thread_ids=subscribed_thread_ids,
            comment_data=self.comment,
            thread_data=self.thread,
        )
        user = mock.Mock()
        comment = cc.Comment.find(id=self.comment['id']).retrieve()
        site = Site.objects.get_current()
        site_config = SiteConfigurationFactory.create(site=site)
        site_config.site_values[ENABLE_FORUM_NOTIFICATIONS_FOR_SITE_KEY] = True
        site_config.save()
        with mock.patch(
                'lms.djangoapps.discussion.signals.handlers.get_current_site',
                return_value=site):
            comment_created.send(sender=None, user=user, post=comment)

        if user_subscribed:
            expected_message_context = get_base_template_context(site)
            expected_message_context.update({
                'comment_author_id':
                self.comment_author.id,
                'comment_body':
                self.comment['body'],
                'comment_created_at':
                ONE_HOUR_AGO,
                'comment_id':
                self.comment['id'],
                'comment_username':
                self.comment_author.username,
                'course_id':
                self.course.id,
                'thread_author_id':
                self.thread_author.id,
                'thread_created_at':
                TWO_HOURS_AGO,
                'thread_id':
                self.discussion_id,
                'thread_title':
                'thread-title',
                'thread_username':
                self.thread_author.username,
                'thread_commentable_id':
                self.thread['commentable_id'],
                'post_link':
                'https://{}{}'.format(site.domain,
                                      self.mock_permalink.return_value),
                'site':
                site,
                'site_id':
                site.id
            })
            expected_recipient = Recipient(self.thread_author.username,
                                           self.thread_author.email)
            actual_message = self.mock_ace_send.call_args_list[0][0][0]
            assert expected_message_context == actual_message.context
            assert expected_recipient == actual_message.recipient
            assert self.course.language == actual_message.language
            self._assert_rendered_email(actual_message)

        else:
            assert not self.mock_ace_send.called
コード例 #3
0
def do_email_change_request(user, new_email, activation_key=None, secondary_email_change_request=False):
    """
    Given a new email for a user, does some basic verification of the new address and sends an activation message
    to the new address. If any issues are encountered with verification or sending the message, a ValueError will
    be thrown.
    """
    # if activation_key is not passing as an argument, generate a random key
    if not activation_key:
        activation_key = uuid.uuid4().hex

    confirm_link = reverse('confirm_email_change', kwargs={'key': activation_key, })

    if secondary_email_change_request:
        PendingSecondaryEmailChange.objects.update_or_create(
            user=user,
            defaults={
                'new_secondary_email': new_email,
                'activation_key': activation_key,
            }
        )
        confirm_link = reverse('activate_secondary_email', kwargs={'key': activation_key})
    else:
        PendingEmailChange.objects.update_or_create(
            user=user,
            defaults={
                'new_email': new_email,
                'activation_key': activation_key,
            }
        )

    use_https = theming_helpers.get_current_request().is_secure()

    site = Site.objects.get_current()
    message_context = get_base_template_context(site)
    message_context.update({
        'old_email': user.email,
        'new_email': new_email,
        'confirm_link': '{protocol}://{site}{link}'.format(
            protocol='https' if use_https else 'http',
            site=configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME),
            link=confirm_link,
        ),
    })

    if secondary_email_change_request:
        msg = RecoveryEmailCreate().personalize(
            recipient=Recipient(user.id, new_email),
            language=preferences_api.get_user_preference(user, LANGUAGE_KEY),
            user_context=message_context,
        )
    else:
        msg = EmailChange().personalize(
            recipient=Recipient(user.id, new_email),
            language=preferences_api.get_user_preference(user, LANGUAGE_KEY),
            user_context=message_context,
        )

    try:
        ace.send(msg)
    except Exception:
        from_address = configuration_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL)
        log.error('Unable to send email activation link to user from "%s"', from_address, exc_info=True)
        raise ValueError(_('Unable to send email activation link. Please try again later.'))  # lint-amnesty, pylint: disable=raise-missing-from

    if not secondary_email_change_request:
        # When the email address change is complete, a "edx.user.settings.changed" event will be emitted.
        # But because changing the email address is multi-step, we also emit an event here so that we can
        # track where the request was initiated.
        tracker.emit(
            SETTING_CHANGE_INITIATED,
            {
                "setting": "email",
                "old": message_context['old_email'],
                "new": message_context['new_email'],
                "user_id": user.id,
            }
        )
コード例 #4
0
def password_change_request_handler(request):
    """Handle password change requests originating from the account page.

    Uses the Account API to email the user a link to the password reset page.

    Note:
        The next step in the password reset process (confirmation) is currently handled
        by student.views.password_reset_confirm_wrapper, a custom wrapper around Django's
        password reset confirmation view.

    Args:
        request (HttpRequest)

    Returns:
        HttpResponse: 200 if the email was sent successfully
        HttpResponse: 400 if there is no 'email' POST parameter
        HttpResponse: 403 if the client has been rate limited
        HttpResponse: 405 if using an unsupported HTTP method

    Example usage:

        POST /account/password

    """

    limiter = BadRequestRateLimiter()
    if limiter.is_rate_limit_exceeded(request):
        AUDIT_LOG.warning("Password reset rate limit exceeded")
        return HttpResponseForbidden()

    user = request.user
    # Prefer logged-in user's email
    email = user.email if user.is_authenticated else request.POST.get('email')

    if email:
        try:
            from openedx.core.djangoapps.user_api.accounts.api import request_password_change
            request_password_change(email, request.is_secure())
            user = user if user.is_authenticated else User.objects.get(
                email=email)
            destroy_oauth_tokens(user)
        except UserNotFound:
            AUDIT_LOG.info("Invalid password reset attempt")
            # Increment the rate limit counter
            limiter.tick_bad_request_counter(request)

            # If enabled, send an email saying that a password reset was attempted, but that there is
            # no user associated with the email
            if configuration_helpers.get_value(
                    'ENABLE_PASSWORD_RESET_FAILURE_EMAIL',
                    settings.FEATURES['ENABLE_PASSWORD_RESET_FAILURE_EMAIL']):
                site = get_current_site()
                message_context = get_base_template_context(site)

                message_context.update({
                    'failed': True,
                    'request':
                    request,  # Used by google_analytics_tracking_pixel
                    'email_address': email,
                })

                msg = PasswordReset().personalize(
                    recipient=Recipient(username='', email_address=email),
                    language=settings.LANGUAGE_CODE,
                    user_context=message_context,
                )

                ace.send(msg)
        except UserAPIInternalError as err:
            log.exception(
                'Error occured during password change for user {email}: {error}'
                .format(email=email, error=err))
            return HttpResponse(_(
                "Some error occured during password change. Please try again"),
                                status=500)

        return HttpResponse(status=200)
    else:
        return HttpResponseBadRequest(_("No email address provided."))
コード例 #5
0
ファイル: views.py プロジェクト: srijan-96/edx-platform-1
    def post(self, request):
        """
        POST /api/user/v1/accounts/deactivate_logout/

        Marks the user as having no password set for deactivation purposes,
        and logs the user out.
        """
        user_model = get_user_model()
        try:
            # Get the username from the request and check that it exists
            verify_user_password_response = self._verify_user_password(request)
            if verify_user_password_response.status_code != status.HTTP_204_NO_CONTENT:
                return verify_user_password_response
            with transaction.atomic():
                UserRetirementStatus.create_retirement(request.user)
                # Unlink LMS social auth accounts
                UserSocialAuth.objects.filter(user_id=request.user.id).delete()
                # Change LMS password & email
                user_email = request.user.email
                request.user.email = get_retired_email_by_email(
                    request.user.email)
                request.user.save()
                _set_unusable_password(request.user)
                # TODO: Unlink social accounts & change password on each IDA.
                # Remove the activation keys sent by email to the user for account activation.
                Registration.objects.filter(user=request.user).delete()
                # Add user to retirement queue.
                # Delete OAuth tokens associated with the user.
                retire_dop_oauth2_models(request.user)
                retire_dot_oauth2_models(request.user)

                try:
                    # Send notification email to user
                    site = Site.objects.get_current()
                    notification_context = get_base_template_context(site)
                    notification_context.update(
                        {'full_name': request.user.profile.name})
                    notification = DeletionNotificationMessage().personalize(
                        recipient=Recipient(username='',
                                            email_address=user_email),
                        language=request.user.profile.language,
                        user_context=notification_context,
                    )
                    ace.send(notification)
                except Exception as exc:
                    log.exception(
                        'Error sending out deletion notification email')
                    raise

                # Log the user out.
                logout(request)
            return Response(status=status.HTTP_204_NO_CONTENT)
        except KeyError:
            return Response(u'Username not specified.',
                            status=status.HTTP_404_NOT_FOUND)
        except user_model.DoesNotExist:
            return Response(u'The user "{}" does not exist.'.format(
                request.user.username),
                            status=status.HTTP_404_NOT_FOUND)
        except Exception as exc:  # pylint: disable=broad-except
            return Response(text_type(exc),
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)
コード例 #6
0
ファイル: enrollment.py プロジェクト: uetuluk/edx-platform
def send_mail_to_student(student, param_dict, language=None):
    """
    Construct the email using templates and then send it.
    `student` is the student's email address (a `str`),

    `param_dict` is a `dict` with keys
    [
        `site_name`: name given to edX instance (a `str`)
        `registration_url`: url for registration (a `str`)
        `display_name` : display name of a course (a `str`)
        `course_id`: id of course (a `str`)
        `auto_enroll`: user input option (a `str`)
        `course_url`: url of course (a `str`)
        `user_id`: LMS user ID of student (an `int`) - None if unknown
        `email_address`: email of student (a `str`)
        `full_name`: student full name (a `str`)
        `message_type`: type of email to send and template to use (a `str`)
        `is_shib_course`: (a `boolean`)
    ]

    `language` is the language used to render the email. If None the language
    of the currently-logged in user (that is, the user sending the email) will
    be used.

    Returns a boolean indicating whether the email was sent successfully.
    """

    # Add some helpers and microconfig subsitutions
    if 'display_name' in param_dict:
        param_dict['course_name'] = param_dict['display_name']
    elif 'course' in param_dict:
        param_dict['course_name'] = Text(param_dict['course'].display_name_with_default)

    param_dict['site_name'] = configuration_helpers.get_value(
        'SITE_NAME',
        param_dict['site_name']
    )

    # Extract an LMS user ID for the student, if possible.
    # ACE needs the user ID to be able to send email via Braze.
    lms_user_id = 0
    if 'user_id' in param_dict and param_dict['user_id'] is not None and param_dict['user_id'] > 0:
        lms_user_id = param_dict['user_id']

    # see if there is an activation email template definition available as configuration,
    # if so, then render that
    message_type = param_dict['message_type']

    ace_emails_dict = {
        'account_creation_and_enrollment': AccountCreationAndEnrollment,
        'add_beta_tester': AddBetaTester,
        'allowed_enroll': AllowedEnroll,
        'allowed_unenroll': AllowedUnenroll,
        'enrolled_enroll': EnrollEnrolled,
        'enrolled_unenroll': EnrolledUnenroll,
        'remove_beta_tester': RemoveBetaTester,
    }

    message_class = ace_emails_dict[message_type]
    message = message_class().personalize(
        recipient=Recipient(lms_user_id=lms_user_id, email_address=student),
        language=language,
        user_context=param_dict,
    )

    ace.send(message)
コード例 #7
0
ファイル: test_tasks.py プロジェクト: colima/edx-platform
    def test_send_discussion_email_notification(self, user_subscribed):
        with mock_the_things() as mocked_items:
            mock_request, mock_ace_send, mock_permalink = mocked_items
            if user_subscribed:
                non_matching_id = 'not-a-match'
                # with per_page left with a default value of 1, this ensures
                # that we test a multiple page result when calling
                # comment_client.User.subscribed_threads()
                mock_request.side_effect = make_mock_responder([non_matching_id, self.discussion_id])
            else:
                mock_request.side_effect = make_mock_responder([])

            now = datetime.utcnow()
            one_hour_ago = now - timedelta(hours=1)
            thread = mock.Mock(
                id=self.discussion_id,
                course_id=self.course.id,
                created_at=one_hour_ago,
                title='thread-title',
                user_id=self.thread_author.id,
                username=self.thread_author.username,
                commentable_id='thread-commentable-id'
            )
            comment = mock.Mock(
                id='comment-id',
                body='comment-body',
                created_at=now,
                thread=thread,
                user_id=self.comment_author.id,
                username=self.comment_author.username
            )
            user = mock.Mock()

            with waffle().override(FORUM_RESPONSE_NOTIFICATIONS):
                comment_created.send(sender=None, user=user, post=comment)

            if user_subscribed:
                expected_message_context = get_base_template_context(Site.objects.get_current())
                expected_message_context.update({
                    'comment_author_id': self.comment_author.id,
                    'comment_body': 'comment-body',
                    'comment_created_at': now,
                    'comment_id': 'comment-id',
                    'comment_username': self.comment_author.username,
                    'course_id': self.course.id,
                    'thread_author_id': self.thread_author.id,
                    'thread_created_at': one_hour_ago,
                    'thread_id': self.discussion_id,
                    'thread_title': 'thread-title',
                    'thread_username': self.thread_author.username,
                    'thread_commentable_id': 'thread-commentable-id',
                    'post_link': urljoin(Site.objects.get_current().domain, mock_permalink.return_value),
                    'site': Site.objects.get_current(),
                    'site_id': Site.objects.get_current().id,
                })
                ga_tracking_pixel_url = _generate_ga_pixel_url(expected_message_context)
                expected_message_context.update({'ga_tracking_pixel_url': ga_tracking_pixel_url})
                expected_recipient = Recipient(self.thread_author.username, self.thread_author.email)
                actual_message = mock_ace_send.call_args_list[0][0][0]
                self.assertEqual(expected_message_context, actual_message.context)
                self.assertEqual(expected_recipient, actual_message.recipient)
                self.assertEqual(self.course.language, actual_message.language)
            else:
                self.assertFalse(mock_ace_send.called)
コード例 #8
0
    def post(self, request, **kwargs):
        #from student.forms import send_password_reset_email_for_user
        from openedx.core.djangoapps.ace_common.template_context import get_base_template_context
        from openedx.core.djangoapps.theming.helpers import get_current_site
        from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
        from student.message_types import PasswordReset
        from django.contrib.auth.tokens import default_token_generator
        from django.utils.http import int_to_base36
        from edx_ace.recipient import Recipient
        from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
        from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
        from edx_ace import ace
        self.data = request.POST.dict()
        if not ('uservalue' and 'sendotptype' in self.data):
            return JsonResponse({
                "status": 400,
                "message": "Please enter Valid Mobile Number or Email Address or password",
            })
        if self.data.get('sendotptype') == "mobile":
            mobile = self.data.get('uservalue')
            user = User.objects.get(extrafields__phone=mobile)
            email = user.email
        else:
            email = self.data.get('uservalue')
        if not email:
            return JsonResponse({
                "status": 400,
                "message": "Email id can not be blank",
            })

        user = User.objects.get(email=email)
        try:
            site = get_current_site()
            message_context = get_base_template_context(site)
            message_context.update({
                'request': request,  # Used by google_analytics_tracking_pixel
                # TODO: This overrides `platform_name` from `get_base_template_context` to make the tests passes
                'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME),
                'reset_link': '{protocol}://{site}{link}'.format(
                    protocol='https',
                    site=configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME),
                    link=reverse('password_reset_confirm', kwargs={
                        'uidb36': int_to_base36(user.id),
                        'token': default_token_generator.make_token(user),
                    }),
                )
            })
            msg = PasswordReset().personalize(
                recipient=Recipient(user.username, user.email),
                language=get_user_preference(user, LANGUAGE_KEY),
               user_context=message_context,
            )
            ace.send(msg)
            return JsonResponse({
                "status": 200,
                "message": "We sent mail in you email",
           })

        except Exception as e:
            return JsonResponse({
                "status": 400,
                "message": "Something error in sending mail",
                "error": "err2",
            })
コード例 #9
0
def send_ace_message(goal):
    """
    Send an email reminding users to stay on track for their learning goal in this course

    Arguments:
        goal (CourseGoal): Goal object
    """
    user = goal.user
    try:
        course = CourseOverview.get_from_id(goal.course_key)
    except CourseOverview.DoesNotExist:
        log.error("Goal Reminder course not found.")

    course_name = course.display_name

    site = Site.objects.get_current()
    message_context = get_base_template_context(site)

    course_home_url = get_learning_mfe_home_url(course_key=goal.course_key,
                                                url_fragment='home')

    goals_unsubscribe_url = f'{settings.LEARNING_MICROFRONTEND_URL}/goal-unsubscribe/{goal.unsubscribe_token}'

    language = get_user_preference(user, LANGUAGE_KEY)

    # Code to allow displaying different banner images for different languages
    # However, we'll likely want to develop a better way to do this within edx-ace
    image_url = settings.STATIC_URL
    if image_url:
        # If the image url is a relative url prepend the LMS ROOT
        if 'http' not in image_url:
            image_url = settings.LMS_ROOT_URL + settings.STATIC_URL
        image_url += 'images/'

        if language and language in ['es', 'es-419']:
            image_url += 'spanish-'

    message_context.update({
        'email':
        user.email,
        'platform_name':
        configuration_helpers.get_value('PLATFORM_NAME',
                                        settings.PLATFORM_NAME),
        'course_name':
        course_name,
        'days_per_week':
        goal.days_per_week,
        'course_url':
        course_home_url,
        'goals_unsubscribe_url':
        goals_unsubscribe_url,
        'image_url':
        image_url,
        'unsubscribe_url':
        None,  # We don't want to include the default unsubscribe link
        'omit_unsubscribe_link':
        True,
        'courses_url':
        getattr(settings, 'ACE_EMAIL_COURSES_URL', None),
        'programs_url':
        getattr(settings, 'ACE_EMAIL_PROGRAMS_URL', None),
    })

    msg = Message(
        name="goalreminder",
        app_label="course_goals",
        recipient=Recipient(user.id, user.email),
        language=language,
        context=message_context,
        options={'transactional': True},
    )

    with emulate_http_request(site, user):
        ace.send(msg)
コード例 #10
0
ファイル: test_tasks.py プロジェクト: rajgauttam/edx-platform
    def test_send_discussion_email_notification(self, user_subscribed):
        if user_subscribed:
            non_matching_id = 'not-a-match'
            # with per_page left with a default value of 1, this ensures
            # that we test a multiple page result when calling
            # comment_client.User.subscribed_threads()
            subscribed_thread_ids = [non_matching_id, self.discussion_id]
        else:
            subscribed_thread_ids = []

        self.mock_request.side_effect = make_mock_responder(
            subscribed_thread_ids=subscribed_thread_ids,
            comment_data=self.comment,
            thread_data=self.thread,
        )
        user = mock.Mock()
        comment = cc.Comment.find(id=self.comment['id']).retrieve()
        site = Site.objects.get_current()
        with waffle().override(FORUM_RESPONSE_NOTIFICATIONS):
            with mock.patch(
                    'lms.djangoapps.discussion.signals.handlers.get_current_site',
                    return_value=site):
                comment_created.send(sender=None, user=user, post=comment)

        if user_subscribed:
            expected_message_context = get_base_template_context(site)
            expected_message_context.update({
                'comment_author_id':
                self.comment_author.id,
                'comment_body':
                self.comment['body'],
                'comment_created_at':
                ONE_HOUR_AGO,
                'comment_id':
                self.comment['id'],
                'comment_username':
                self.comment_author.username,
                'course_id':
                self.course.id,
                'thread_author_id':
                self.thread_author.id,
                'thread_created_at':
                TWO_HOURS_AGO,
                'thread_id':
                self.discussion_id,
                'thread_title':
                'thread-title',
                'thread_username':
                self.thread_author.username,
                'thread_commentable_id':
                self.thread['commentable_id'],
                'post_link':
                urljoin(site.domain, self.mock_permalink.return_value),
                'site':
                site,
                'site_id':
                site.id
            })
            expected_message_context[
                'ga_tracking_pixel_url'] = _generate_ga_pixel_url(
                    expected_message_context)
            expected_recipient = Recipient(self.thread_author.username,
                                           self.thread_author.email)
            actual_message = self.mock_ace_send.call_args_list[0][0][0]
            self.assertEqual(expected_message_context, actual_message.context)
            self.assertEqual(expected_recipient, actual_message.recipient)
            self.assertEqual(self.course.language, actual_message.language)
        else:
            self.assertFalse(self.mock_ace_send.called)