chore: handle save-for-later PII (#29696)

Added PII annotations for email fields in save-for-later models and
also added user retirement signal receiver to remove related objects.
This commit is contained in:
Waheed Ahmed
2021-12-31 11:31:53 +05:00
committed by GitHub
parent 4f49475b94
commit 9aa0cc44a2
4 changed files with 74 additions and 2 deletions

View File

@@ -7,14 +7,30 @@ from model_utils.models import TimeStampedModel
from django.db import models
from opaque_keys.edx.django.models import CourseKeyField
from openedx.core.djangolib.model_mixins import DeletableByUserValue
class SavedCourse(TimeStampedModel):
class SavedCourse(DeletableByUserValue, TimeStampedModel):
"""
Tracks save course by email.
.. pii: Stores email address of the User.
.. pii_types: email_address
.. pii_retirement: local_api
"""
user_id = models.IntegerField(null=True, blank=True)
email = models.EmailField(db_index=True)
course_id = CourseKeyField(max_length=255, db_index=True)
class SavedProgram(TimeStampedModel):
class SavedProgram(DeletableByUserValue, TimeStampedModel):
"""
Tracks save program by email.
.. pii: Stores email address of the User.
.. pii_types: email_address
.. pii_retirement: local_api
"""
user_id = models.IntegerField(null=True, blank=True)
email = models.EmailField(db_index=True)
program_uuid = models.UUIDField()

View File

@@ -0,0 +1,14 @@
"""
Signal handler for save for later
"""
from django.dispatch.dispatcher import receiver
from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL
from .models import SavedCourse, SavedProgram
@receiver(USER_RETIRE_LMS_CRITICAL)
def _listen_for_lms_retire(sender, **kwargs): # pylint: disable=unused-argument
user = kwargs.get('user')
SavedCourse.delete_by_user_value(user.id, field='user_id')
SavedProgram.delete_by_user_value(user.id, field='user_id')

View File

@@ -0,0 +1,42 @@
"""
Unit tests for the signals
"""
from uuid import uuid4
from django.test import TestCase
from common.djangoapps.student.tests.factories import UserFactory
from ..models import SavedCourse, SavedProgram
from ..signals import _listen_for_lms_retire
class RetirementSignalTest(TestCase):
"""
Tests for the user retirement signal
"""
def setUp(self):
super().setUp()
self.user = UserFactory()
self.email = self.user.email
def _create_objects(self):
"""
Create test objects.
"""
SavedCourse.objects.create(user_id=self.user.id, email=self.email, course_id='course-v1:TestX+TestX101+1T2022')
SavedProgram.objects.create(user_id=self.user.id, email=self.email, program_uuid=uuid4())
assert SavedCourse.objects.filter(email=self.email).exists()
assert SavedProgram.objects.filter(email=self.email).exists()
def test_retire_success(self):
self._create_objects()
_listen_for_lms_retire(sender=self.__class__, user=self.user, email=self.email)
assert not SavedCourse.objects.filter(email=self.email).exists()
assert not SavedProgram.objects.filter(email=self.email).exists()
def test_retire_success_no_entries(self):
assert not SavedCourse.objects.filter(email=self.email).exists()
assert not SavedProgram.objects.filter(email=self.email).exists()
_listen_for_lms_retire(sender=self.__class__, user=self.user, email=self.email)