@@ -0,0 +1,3742 @@
+%22%22%22%0AOne-off script to rehash all retired emails.%0A%0ABackground: We discovered that all prior retired emails were generated based on%0Athe exact capitalization of the original email address, despite the fact that%0Aemails are considered case insensitive in practice.  This led to the%0Apossibility of users registering accounts with effectively retired emails just%0Aby changing the capitalization of the email, because the different%0Acapitalization would hash to a different digest.%0A%0ASolution: Rehash all email addresses using the normalized-case (lowercase)%0Aoriginal emails rather than the possibly mixed-case ones.  This management%0Acommand likely cannot be re-used in the future because eventually we will need%0Ato clean out the UserRetirementStatus table.%0A%22%22%22%0A%0Afrom __future__ import print_function%0A%0Afrom django.conf import settings%0Afrom django.db import transaction%0Afrom django.core.management.base import BaseCommand%0A%0Afrom openedx.core.djangoapps.user_api.models import UserRetirementStatus%0Afrom user_util import user_util%0A%0A%0Aclass Command(BaseCommand):%0A    %22%22%22%0A    Implementation of the bulk_rehash_retired_emails command.%0A    %22%22%22%0A%0A    def add_arguments(self, parser):%0A        parser.add_argument(%0A            '--dry_run',%0A            action='store_true',%0A            help='Print proposed changes, but take no action.'%0A        )%0A%0A    def handle(self, *args, **options):%0A        %22%22%22%0A        Execute the command.%0A        %22%22%22%0A%0A        dry_run = options%5B'dry_run'%5D%0A%0A        retirements = UserRetirementStatus.objects.all().select_related('user')%0A        for retirement in retirements:%0A            original_email = retirement.original_email%0A            old_retired_email = retirement.retired_email%0A            new_retired_email = user_util.get_retired_email(%0A                original_email,%0A                settings.RETIRED_USER_SALTS,%0A                settings.RETIRED_EMAIL_FMT%0A            )%0A%0A            # Sanity check:%0A            if retirement.user.email != old_retired_email:%0A                print(%0A                    'WARNING: Skipping UserRetirementStatus ID %7B%7D / User ID %7B%7D because the user does not appear to '%0A                    'have a retired email address: %7B%7D.'.format(%0A                        retirement.id,%0A                        retirement.user.id,%0A                        retirement.user.email,%0A                    )%0A                )%0A            # If the original email address was already normalized (or all lowercase), the old and new hashes would%0A            # match:%0A            elif old_retired_email == new_retired_email:%0A                print(%0A                    'Skipping UserRetirementStatus ID %7B%7D / User ID %7B%7D because the email hash would not change.'.format(%0A                        retirement.id,%0A                        retirement.user.id,%0A                    )%0A                )%0A            # Found an email to update:%0A            else:%0A                print('Updating UserRetirementStatus ID %7B%7D / User ID %7B%7D to rehash their retired email: %7B%7D -%3E %7B%7D'.format(%0A                    retirement.id,%0A                    retirement.user.id,%0A                    old_retired_email,%0A                    new_retired_email,%0A                ))%0A                if not dry_run:%0A                    # Update and save both the user table and retirement queue table:%0A                    with transaction.atomic():%0A                        retirement.user.email = new_retired_email%0A                        retirement.user.save()%0A                        retirement.retired_email = new_retired_email%0A                        retirement.save()%0A                    # The only other place to update is in sailthru, so make sure to save the logging from this%0A                    # management command to make it possible to update sailthru later.%0A

Add one-off management command to rehash retired emails