EDUCATOR-2753 | Add calls to retire secondary user data to the /accounts/retire endpoint.

This commit is contained in:
Alex Dusenbery
2018-04-25 16:12:32 -04:00
committed by Alex Dusenbery
parent 137b9da22b
commit 95d5b13714
4 changed files with 107 additions and 1 deletions

View File

@@ -0,0 +1,21 @@
# pylint:disable=missing-docstring
import factory
from student.tests.factories import UserFactory
from survey.models import SurveyAnswer, SurveyForm
class SurveyFormFactory(factory.DjangoModelFactory):
class Meta(object):
model = SurveyForm
name = 'Test Survey Form'
form = '<form>First name:<input type="text" name="firstname"/></form>'
class SurveyAnswerFactory(factory.DjangoModelFactory):
class Meta(object):
model = SurveyAnswer
user = factory.SubFactory(UserFactory)
form = factory.SubFactory(SurveyFormFactory)

View File

@@ -8,7 +8,14 @@ import factory
from factory.fuzzy import FuzzyText
import pytz
from openedx.core.djangoapps.credit.models import CreditProvider, CreditEligibility, CreditCourse, CreditRequest
from openedx.core.djangoapps.credit.models import (
CreditProvider,
CreditEligibility,
CreditCourse,
CreditRequest,
CreditRequirement,
CreditRequirementStatus,
)
from util.date_utils import to_timestamp
@@ -20,6 +27,21 @@ class CreditCourseFactory(factory.DjangoModelFactory):
enabled = True
class CreditRequirementFactory(factory.DjangoModelFactory):
class Meta(object):
model = CreditRequirement
course = factory.SubFactory(CreditCourseFactory)
class CreditRequirementStatusFactory(factory.DjangoModelFactory):
class Meta(object):
model = CreditRequirementStatus
requirement = factory.SubFactory(CreditRequirementFactory)
status = CreditRequirementStatus.REQUIREMENT_STATUS_CHOICES[0][0]
class CreditProviderFactory(factory.DjangoModelFactory):
class Meta(object):
model = CreditProvider

View File

@@ -28,6 +28,7 @@ from integrated_channels.sap_success_factors.models import (
)
import mock
from nose.plugins.attrib import attr
from opaque_keys.edx.keys import CourseKey
import pytest
import pytz
from rest_framework import status
@@ -37,6 +38,8 @@ from social_django.models import UserSocialAuth
from entitlements.models import CourseEntitlementSupportDetail
from entitlements.tests.factories import CourseEntitlementFactory
from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory
from openedx.core.djangoapps.course_groups.models import CourseUserGroup, UnregisteredLearnerCohortAssignments
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.user_api.accounts import ACCOUNT_VISIBILITY_PREF_KEY
from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_MAILINGS
@@ -45,6 +48,7 @@ from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
from openedx.core.lib.token_utils import JwtBuilder
from student.models import (
CourseEnrollmentAllowed,
PendingEmailChange,
SocialLink,
UserProfile,
@@ -54,12 +58,16 @@ from student.models import (
from student.tests.factories import (
TEST_PASSWORD,
ContentTypeFactory,
CourseEnrollmentAllowedFactory,
PendingEmailChangeFactory,
PermissionFactory,
SuperuserFactory,
UserFactory
)
from .. import ALL_USERS_VISIBILITY, PRIVATE_VISIBILITY
from ..views import AccountRetirementView, USER_PROFILE_PII
from ...tests.factories import UserOrgTagFactory
TEST_PROFILE_IMAGE_UPLOADED_AT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo=pytz.UTC)
@@ -1677,6 +1685,26 @@ class TestAccountRetirementPost(RetirementTestCase):
comments='A comment containing potential PII.'
)
# Misc. setup
self.photo_verification = SoftwareSecurePhotoVerificationFactory.create(user=self.test_user)
PendingEmailChangeFactory.create(user=self.test_user)
UserOrgTagFactory.create(user=self.test_user, key='foo', value='bar')
UserOrgTagFactory.create(user=self.test_user, key='cat', value='dog')
CourseEnrollmentAllowedFactory.create(email=self.original_email)
self.course_key = CourseKey.from_string('course-v1:edX+DemoX+Demo_Course')
self.cohort = CourseUserGroup.objects.create(
name="TestCohort",
course_id=self.course_key,
group_type=CourseUserGroup.COHORT
)
self.cohort_assignment = UnregisteredLearnerCohortAssignments.objects.create(
course_user_group=self.cohort,
course_id=self.course_key,
email=self.original_email
)
# setup for doing POST from test client
self.headers = self.build_jwt_headers(self.test_superuser)
self.headers['content_type'] = "application/json"
@@ -1771,6 +1799,13 @@ class TestAccountRetirementPost(RetirementTestCase):
self._pending_enterprise_customer_user_assertions()
self._entitlement_support_detail_assertions()
self._photo_verification_assertions()
self.assertFalse(PendingEmailChange.objects.filter(user=self.test_user).exists())
self.assertFalse(UserOrgTag.objects.filter(user=self.test_user).exists())
self.assertFalse(CourseEnrollmentAllowed.objects.filter(email=self.original_email).exists())
self.assertFalse(UnregisteredLearnerCohortAssignments.objects.filter(email=self.original_email).exists())
def test_deletes_pii_from_user_profile(self):
for model_field, value_to_assign in USER_PROFILE_PII.iteritems():
if value_to_assign == '':
@@ -1866,3 +1901,12 @@ class TestAccountRetirementPost(RetirementTestCase):
"""
self.entitlement_support_detail.refresh_from_db()
self.assertEqual('', self.entitlement_support_detail.comments)
def _photo_verification_assertions(self):
"""
Helper method for asserting that ``SoftwareSecurePhotoVerification`` objects are retired.
"""
self.photo_verification.refresh_from_db()
self.assertEqual(self.test_user, self.photo_verification.user)
for field in ('name', 'face_image_url', 'photo_id_image_url', 'photo_id_key'):
self.assertEqual('', getattr(self.photo_verification, field))

View File

@@ -27,6 +27,8 @@ from six import text_type
from social_django.models import UserSocialAuth
from entitlements.models import CourseEntitlement
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
from openedx.core.djangoapps.course_groups.models import UnregisteredLearnerCohortAssignments
from openedx.core.djangoapps.profile_images.images import remove_profile_images
from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_names, set_has_profile_image
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in
@@ -36,6 +38,8 @@ from openedx.core.lib.api.authentication import (
)
from openedx.core.lib.api.parsers import MergePatchParser
from student.models import (
CourseEnrollmentAllowed,
PendingEmailChange,
User,
UserProfile,
get_potentially_retired_user_by_username,
@@ -594,16 +598,31 @@ class AccountRetirementView(ViewSet):
user = retirement_status.user
retired_username = retirement_status.retired_username or get_retired_username_by_username(username)
retired_email = retirement_status.retired_email or get_retired_email_by_email(user.email)
original_email = retirement_status.original_email
# Retire core user/profile information
self.clear_pii_from_userprofile(user)
self.delete_users_profile_images(user)
self.delete_users_country_cache(user)
# Retire data from Enterprise models
self.retire_users_data_sharing_consent(username, retired_username)
self.retire_sapsf_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
SoftwareSecurePhotoVerification.retire_user(user.id)
PendingEmailChange.delete_by_user_value(user, field='user')
UserOrgTag.delete_by_user_value(user, field='user')
# Retire any objects linked to the user via their original email
CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email')
UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email')
# TODO: Password Reset links - https://openedx.atlassian.net/browse/PLAT-2104
# TODO: Delete OAuth2 records - https://openedx.atlassian.net/browse/EDUCATOR-2703
user.first_name = ''
user.last_name = ''
user.is_active = False