From 47a920d5b9e3e9aade8ff134ed3380da0ef79805 Mon Sep 17 00:00:00 2001 From: Muhammad Adeel Tajamul <77053848+muhammadadeeltajamul@users.noreply.github.com> Date: Tue, 29 Apr 2025 11:29:33 +0500 Subject: [PATCH] feat: added a policy to prevent sending ace messages to disabled users (#36584) --- lms/djangoapps/bulk_email/messages.py | 1 + .../commands/goal_reminder_email.py | 10 ++++--- .../core/djangoapps/ace_common/policies.py | 29 +++++++++++++++++++ .../notifications/email/message_type.py | 1 + openedx/core/djangoapps/schedules/tasks.py | 1 + .../djangoapps/user_authn/message_types.py | 1 + setup.py | 1 + 7 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 openedx/core/djangoapps/ace_common/policies.py diff --git a/lms/djangoapps/bulk_email/messages.py b/lms/djangoapps/bulk_email/messages.py index 8052ca0158..c1c16a2d51 100644 --- a/lms/djangoapps/bulk_email/messages.py +++ b/lms/djangoapps/bulk_email/messages.py @@ -83,6 +83,7 @@ class ACEEmail(CourseEmailMessage): language=email_context['course_language'], user_context={"name": email_context['name']}, ) + message.options['skip_disable_user_policy'] = True self.message = message def send(self): diff --git a/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py b/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py index 7b3f3f598b..cb7b5cef37 100644 --- a/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py +++ b/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py @@ -105,16 +105,18 @@ def send_ace_message(goal, session_id): 'programs_url': getattr(settings, 'ACE_EMAIL_PROGRAMS_URL', None), }) - options = {'transactional': True} + options = { + 'transactional': True, + 'skip_disable_user_policy': True + } is_ses_enabled = ENABLE_SES_FOR_GOALREMINDER.is_enabled(goal.course_key) if is_ses_enabled: - options = { - 'transactional': True, + options.update({ 'from_address': settings.LMS_COMM_DEFAULT_FROM_EMAIL, 'override_default_channel': 'django_email', - } + }) msg = Message( name="goalreminder", diff --git a/openedx/core/djangoapps/ace_common/policies.py b/openedx/core/djangoapps/ace_common/policies.py new file mode 100644 index 0000000000..6ad745b86d --- /dev/null +++ b/openedx/core/djangoapps/ace_common/policies.py @@ -0,0 +1,29 @@ +"""Disable User Email OptOut Policy""" + +import logging + +from django.contrib.auth import get_user_model +from edx_ace.channel import ChannelType +from edx_ace.policy import Policy, PolicyResult + + +User = get_user_model() +log = logging.getLogger(__name__) + + +class DisableUserOptout(Policy): + """ + Skips sending ace messages to disabled users + """ + def check(self, message): + """ + Checks if the user is disabled and if so, skips sending the message + """ + skip_disable_user_policy = message.options.get('skip_disable_user_policy', False) + if skip_disable_user_policy: + return PolicyResult(deny=set()) + user = User.objects.get(id=message.recipient.lms_user_id) + if user.has_usable_password(): + return PolicyResult(deny=set()) + log.info(f"===> User is disabled - {user.email} - {message.name}") + return PolicyResult(deny=set(ChannelType)) diff --git a/openedx/core/djangoapps/notifications/email/message_type.py b/openedx/core/djangoapps/notifications/email/message_type.py index 655363248b..e1c0c8ba19 100644 --- a/openedx/core/djangoapps/notifications/email/message_type.py +++ b/openedx/core/djangoapps/notifications/email/message_type.py @@ -16,3 +16,4 @@ class EmailNotificationMessageType(MessageType): super().__init__(*args, **kwargs) self.options['transactional'] = True self.options['from_address'] = settings.NOTIFICATIONS_DEFAULT_FROM_EMAIL + self.options['skip_disable_user_policy'] = True diff --git a/openedx/core/djangoapps/schedules/tasks.py b/openedx/core/djangoapps/schedules/tasks.py index 6a999d3dd8..628276dc22 100644 --- a/openedx/core/djangoapps/schedules/tasks.py +++ b/openedx/core/djangoapps/schedules/tasks.py @@ -274,6 +274,7 @@ def _schedule_send(msg_str, site_id, delivery_config_var, log_prefix): # lint-a site = Site.objects.select_related('configuration').get(pk=site_id) if _is_delivery_enabled(site, delivery_config_var, log_prefix): msg = Message.from_string(msg_str) + msg.options['skip_disable_user_policy'] = True user = User.objects.get(id=msg.recipient.lms_user_id) if not user.has_usable_password(): diff --git a/openedx/core/djangoapps/user_authn/message_types.py b/openedx/core/djangoapps/user_authn/message_types.py index 83391374a1..51af68e00c 100644 --- a/openedx/core/djangoapps/user_authn/message_types.py +++ b/openedx/core/djangoapps/user_authn/message_types.py @@ -14,6 +14,7 @@ class PasswordReset(BaseMessageType): # pylint: disable=unsupported-assignment-operation self.options['transactional'] = True + self.options['skip_disable_user_policy'] = True class PasswordResetSuccess(BaseMessageType): diff --git a/setup.py b/setup.py index 3b8f8c5949..5b9f020ac3 100644 --- a/setup.py +++ b/setup.py @@ -132,6 +132,7 @@ setup( "openedx.ace.policy": [ "bulk_email_optout = lms.djangoapps.bulk_email.policies:CourseEmailOptout", "course_push_notification_optout = openedx.core.djangoapps.notifications.policies:CoursePushNotificationOptout", # lint-amnesty, pylint: disable=line-too-long + "disabled_user_optout = openedx.core.djangoapps.ace_common.policies:DisableUserOptout", ], "openedx.call_to_action": [ "personalized_learner_schedules = openedx.features.personalized_learner_schedules.call_to_action:PersonalizedLearnerScheduleCallToAction" # lint-amnesty, pylint: disable=line-too-long