diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 76cdbbba52..f24e1e5977 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -29,7 +29,7 @@ from django.contrib.auth.models import User from django.contrib.auth.signals import user_logged_in, user_logged_out from django.core.cache import cache from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist -from django.db import IntegrityError, models +from django.db import IntegrityError, models, transaction from django.db.models import Count, Q from django.db.models.signals import post_save, pre_save from django.dispatch import receiver @@ -900,6 +900,20 @@ class PasswordHistory(models.Model): return True + @classmethod + def retire_user(cls, user_id): + """ + Updates the password in all rows corresponding to a user + to an empty string as part of removing PII for user retirement. + """ + changed_password = False + with transaction.atomic(): + for row, _ in cls.objects.filter(user_id=user_id): + changed_password = True + row.password = "" + + return changed_password + class LoginFailures(models.Model): """ diff --git a/common/djangoapps/student/tests/test_password_history.py b/common/djangoapps/student/tests/test_password_history.py index 74769ca5b2..5245441fcc 100644 --- a/common/djangoapps/student/tests/test_password_history.py +++ b/common/djangoapps/student/tests/test_password_history.py @@ -203,3 +203,20 @@ class TestPasswordHistory(TestCase): student = self._user_factory_with_history() self.assertFalse(PasswordHistory.is_password_reset_too_soon(student)) + + def test_retirement(self): + """ + Verify that the user's password history contains no actual + passwords after retirement is called. + """ + user = self._user_factory_with_history() + + # create multiple rows in the password history table + self._change_password(user, "different") + self._change_password(user, "differentagain") + for row in PasswordHistory.objects.filter(user_id=user.id): + self.assertFalse(row.password == "") + + PasswordHistory.retire_user(user.id) + for row in PasswordHistory.objects.filter(user_id=user.id): + self.assertEqual(row.password, "")