Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import datetime
import json
from unittest import mock
from zoneinfo import ZoneInfo

import ddt
from zoneinfo import ZoneInfo
from consent.models import DataSharingConsent
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.contrib.sites.models import Site
Expand Down Expand Up @@ -65,6 +65,7 @@
)
from openedx.core.djangoapps.external_user_ids.models import ExternalIdType
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
from openedx.core.djangoapps.oauth_dispatch.tests.factories import AccessTokenFactory, ApplicationFactory
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.user_api.accounts.views import AccountRetirementPartnerReportView
from openedx.core.djangoapps.user_api.models import (
Expand All @@ -74,7 +75,6 @@
UserRetirementStatus
)
from openedx.core.djangolib.testing.utils import skip_unless_lms
from openedx.core.djangoapps.oauth_dispatch.tests.factories import ApplicationFactory, AccessTokenFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

Expand Down Expand Up @@ -1463,9 +1463,7 @@ def test_retire_user(self, mock_remove_profile_images, mock_get_profile_image_na

assert cache.get(self.cache_key) is None

self._data_sharing_consent_assertions()
self._sapsf_audit_assertions()
self._pending_enterprise_customer_user_assertions()
self._entitlement_support_detail_assertions()

assert not PendingEmailChange.objects.filter(user=self.test_user).exists()
Expand Down Expand Up @@ -1520,18 +1518,25 @@ def test_can_delete_user_profiles_country_cache(self):
AccountRetirementView.delete_users_country_cache(self.test_user)
assert cache.get(self.cache_key) is None

def test_can_retire_users_datasharingconsent(self):
AccountRetirementView.retire_users_data_sharing_consent(self.test_user.username, self.retired_username)
self._data_sharing_consent_assertions()
@mock.patch('openedx.core.djangoapps.user_api.accounts.views.USER_RETIRE_LMS_CRITICAL')
def test_retirement_sends_critical_signal_with_retirement_data(self, mock_signal):
"""
USER_RETIRE_LMS_CRITICAL is sent with retired_username and retired_email kwargs.
"""
data = {'username': self.original_username}
self.post_and_assert_status(data)

mock_signal.send.assert_called_once_with(
sender=mock_signal.send.call_args[1]['sender'],
user=mock_signal.send.call_args[1]['user'],
retired_username=self.retired_username,
retired_email=self.retired_email,
)

def test_can_retire_users_sap_success_factors_audits(self):
AccountRetirementView.retire_sapsf_data_transmission(self.test_user)
self._sapsf_audit_assertions()

def test_can_retire_user_from_pendingenterprisecustomeruser(self):
AccountRetirementView.retire_user_from_pending_enterprise_customer_user(self.test_user, self.retired_email)
self._pending_enterprise_customer_user_assertions()

def test_course_entitlement_support_detail_comments_are_retired(self):
AccountRetirementView.retire_entitlement_support_detail(self.test_user)
self._entitlement_support_detail_assertions()
Expand Down
37 changes: 14 additions & 23 deletions openedx/core/djangoapps/user_api/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
import datetime
import logging
from functools import wraps

from zoneinfo import ZoneInfo
from consent.models import DataSharingConsent

from django.apps import apps
from django.conf import settings
from django.contrib.auth import authenticate, get_user_model, logout
Expand All @@ -25,7 +24,7 @@
from edx_ace.recipient import Recipient
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
from enterprise.models import EnterpriseCourseEnrollment, EnterpriseCustomerUser, PendingEnterpriseCustomerUser
from enterprise.models import EnterpriseCourseEnrollment, EnterpriseCustomerUser
from integrated_channels.degreed.models import DegreedLearnerDataTransmissionAudit
from integrated_channels.sap_success_factors.models import SapSuccessFactorsLearnerDataTransmissionAudit
from rest_framework import permissions, status
Expand All @@ -39,7 +38,6 @@
from wiki.models import ArticleRevision
from wiki.models.pluginbase import RevisionPluginRevision

from common.djangoapps.track import segment
from common.djangoapps.entitlements.models import CourseEntitlement
from common.djangoapps.student.models import ( # lint-amnesty, pylint: disable=unused-import
CourseEnrollmentAllowed,
Expand All @@ -53,9 +51,10 @@
get_retired_email_by_email,
get_retired_username_by_username,
is_email_retired,
is_username_retired,
is_username_retired
)
from common.djangoapps.student.models_api import confirm_name_change, do_name_change_request, get_pending_name_change
from common.djangoapps.track import segment
from lms.djangoapps.certificates.api import clear_pii_from_certificate_records_for_user
from openedx.core.djangoapps.ace_common.template_context import get_base_template_context
from openedx.core.djangoapps.api_admin.models import ApiAccessRequest
Expand All @@ -78,21 +77,21 @@
RetirementStateError,
UserOrgTag,
UserRetirementPartnerReportingStatus,
UserRetirementStatus,
UserRetirementStatus
)
from .api import get_account_settings, update_account_settings
from .permissions import (
CanCancelUserRetirement,
CanDeactivateUser,
CanGetAccountInfo,
CanReplaceUsername,
CanRetireUser,
CanRetireUser
)
from .serializers import (
PendingNameChangeSerializer,
UserRetirementPartnerReportSerializer,
UserRetirementStatusSerializer,
UserSearchEmailSerializer,
UserSearchEmailSerializer
)
from .signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC, USER_RETIRE_MAILINGS
from .utils import create_retirement_request_and_deactivate_account, username_suffix_generator
Expand Down Expand Up @@ -1154,11 +1153,8 @@ def post(self, request):
# Retire user information from any certificate records associated with the learner
self.clear_pii_from_certificate_records(user)

# Retire data from Enterprise models
self.retire_users_data_sharing_consent(username, retired_username)
self.retire_sapsf_data_transmission(user)
self.retire_degreed_data_transmission(user)
self.retire_user_from_pending_enterprise_customer_user(user, retired_email)
self.retire_entitlement_support_detail(user)

# Retire misc. models that may contain PII of this user
Expand All @@ -1169,8 +1165,13 @@ def post(self, request):
CourseEnrollmentAllowed.delete_by_user_value(original_email, field="email")
UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field="email")

# This signal allows code in higher points of LMS to retire the user as necessary
USER_RETIRE_LMS_CRITICAL.send(sender=self.__class__, user=user)
# This signal allows plugins to retire enterprise-specific user data.
USER_RETIRE_LMS_CRITICAL.send(
sender=self.__class__,
user=user,
retired_username=retired_username,
retired_email=retired_email,
)

user.first_name = ""
user.last_name = ""
Expand Down Expand Up @@ -1209,10 +1210,6 @@ def delete_users_country_cache(user):
cache_key = UserProfile.country_cache_key_name(user.id)
cache.delete(cache_key)

@staticmethod
def retire_users_data_sharing_consent(username, retired_username):
DataSharingConsent.objects.filter(username=username).update(username=retired_username)

@staticmethod
def retire_sapsf_data_transmission(user): # lint-amnesty, pylint: disable=missing-function-docstring
for ent_user in EnterpriseCustomerUser.objects.filter(user_id=user.id):
Expand All @@ -1231,10 +1228,6 @@ def retire_degreed_data_transmission(user): # lint-amnesty, pylint: disable=mis
)
audits.update(degreed_user_email="")

@staticmethod
def retire_user_from_pending_enterprise_customer_user(user, retired_email):
PendingEnterpriseCustomerUser.objects.filter(user_email=user.email).update(user_email=retired_email)

@staticmethod
def retire_entitlement_support_detail(user):
"""
Expand Down Expand Up @@ -1307,8 +1300,6 @@ def post(self, request):
# (model_name, column_name)
MODELS_WITH_USERNAME = (
("auth.user", "username"),
("consent.DataSharingConsent", "username"),
("consent.HistoricalDataSharingConsent", "username"),
("credit.CreditEligibility", "username"),
("credit.CreditRequest", "username"),
("credit.CreditRequirementStatus", "username"),
Expand Down