Пример #1
0
def test_username_to_hash_is_normalized(salt_list):
    """
    Make sure identical usernames with different cases map to the same retired username.
    """
    username_mixed = 'ALearnerUserName'
    username_lower = username_mixed.lower()
    retired_username_mixed = user_util.get_retired_username(username_mixed, salt_list)
    retired_username_lower = user_util.get_retired_username(username_lower, salt_list)
    # No matter the case of the input username, the retired username hash should be identical.
    assert retired_username_mixed == retired_username_lower
Пример #2
0
def test_successful_rehash(retire_user_forums, capsys):
    """
    Run the command with users of all different hash statuses, expect success
    """
    users_skipped, users_needing_rehash, retirements = _setup_users()

    call_command('bulk_rehash_retired_usernames')
    output = capsys.readouterr().out

    # Make sure forums was called the correct number of times
    assert retire_user_forums.call_count == 2

    for user in users_skipped:
        assert "User ID {} because the hash would not change.".format(
            user.id) in output

    expected_username_calls = []
    for user in users_needing_rehash:
        retirement = retirements[user.id]
        user.refresh_from_db()
        retirement.refresh_from_db()
        new_retired_username = get_retired_username(
            retirement.original_username, settings.RETIRED_USER_SALTS,
            settings.RETIRED_USERNAME_FMT)
        expected_username_calls.append(call(new_retired_username))

        assert "User ID {} to rehash their retired username".format(
            user.id) in output
        assert new_retired_username == user.username
        assert new_retired_username == retirement.retired_username

    retire_user_forums.assert_has_calls(expected_username_calls)
Пример #3
0
def test_unicode_username_to_hash():
    username = u'ÁĹéáŕńéŕŰśéŕŃáḿéẂíthŰńíćődé'
    retired_username = user_util.get_retired_username(
        username, VALID_SALT_LIST_ONE_SALT)
    assert retired_username != username
    # Since SHA1 is used, the hexadecimal digest length should be 40.
    assert len(retired_username.split('_')[-1]) == 40
Пример #4
0
def test_username_to_hash(salt_list):
    username = '******'
    retired_username = user_util.get_retired_username(username, salt_list)
    assert retired_username != username
    assert retired_username.startswith('_'.join(user_util.RETIRED_USERNAME_DEFAULT_FMT.split('_')[0:-1]))
    # Since SHA1 is used, the hexadecimal digest length should be 40.
    assert len(retired_username.split('_')[-1]) == 40
Пример #5
0
def test_username_to_hash_with_different_format(salt_list):
    username = '******'
    retired_username_fmt = "{}_is_now_the_retired_username"
    retired_username = user_util.get_retired_username(username, salt_list, retired_username_fmt=retired_username_fmt)
    assert retired_username.endswith('_'.join(retired_username_fmt.split('_')[1:]))
    # Since SHA1 is used, the hexadecimal digest length should be 40.
    assert len(retired_username.split('_')[0]) == 40
Пример #6
0
def test_correct_username_hash(salt_list):
    """
    Verify that get_retired_username uses the current salt and returns the expected hash.
    """
    username = '******'
    # Valid retired usernames for the above username when using VALID_SALT_LIST_THREE_SALTS.
    valid_retired_usernames = [
        user_util.RETIRED_USERNAME_DEFAULT_FMT.format(user_util._compute_retired_hash(username.lower(), salt))
        for salt in salt_list
    ]
    retired_username = user_util.get_retired_username(username, salt_list)
    assert retired_username == valid_retired_usernames[-1]
def test_forums_failed(retire_user_forums, capsys):
    """
    Run the command with users of all different hash statuses, expect success
    """
    users_skipped, users_faked, users_needing_rehash, retirements = _setup_users(
    )
    retire_user_forums.side_effect = Exception(
        'something bad happened with forums')

    call_command('bulk_rehash_retired_usernames')
    output = capsys.readouterr().out

    # Make sure forums was called the correct number of times
    assert retire_user_forums.call_count == 2

    for user in users_skipped:
        assert "User ID {} because the user does not appear to have a retired username:"******"User ID {} because the hash would not change.".format(
            user.id) in output

    expected_username_calls = []
    for user in users_needing_rehash:
        retirement = retirements[user.id]
        user.refresh_from_db()
        retirement.refresh_from_db()
        new_retired_username = get_retired_username(
            retirement.original_username, settings.RETIRED_USER_SALTS,
            settings.RETIRED_USERNAME_FMT)
        expected_username_calls.append(call(new_retired_username))

        assert "User ID {} to rehash their retired username".format(
            user.id) in output
        # Confirm that the usernames are *not* updated, due to the forums error
        assert new_retired_username != user.username
        assert new_retired_username != retirement.retired_username

    assert "FAILED! 2 retirements failed to rehash. Retirement IDs:" in output
    retire_user_forums.assert_has_calls(expected_username_calls)
Пример #8
0
def test_forums_404(retire_user_forums, capsys):
    """
    Run the command with users of all different hash statuses, expect success
    """
    users_skipped, users_needing_rehash, retirements = _setup_users()
    retire_user_forums.side_effect = comment_client.utils.CommentClientRequestError(
        'not found', status_codes=404)

    call_command('bulk_rehash_retired_usernames')
    output = capsys.readouterr().out

    # Make sure forums was called the correct number of times
    assert retire_user_forums.call_count == 2

    for user in users_skipped:
        assert "User ID {} because the hash would not change.".format(
            user.id) in output

    expected_username_calls = []
    for user in users_needing_rehash:
        retirement = retirements[user.id]
        user.refresh_from_db()
        retirement.refresh_from_db()
        new_retired_username = get_retired_username(
            retirement.original_username, settings.RETIRED_USER_SALTS,
            settings.RETIRED_USERNAME_FMT)
        expected_username_calls.append(call(new_retired_username))

        assert "User ID {} to rehash their retired username".format(
            user.id) in output
        # Confirm that the usernames *are* updated, since this is a non-blocking forums error
        assert new_retired_username == user.username
        assert new_retired_username == retirement.retired_username

    assert "Success!" in output
    retire_user_forums.assert_has_calls(expected_username_calls)
Пример #9
0
    def handle(self, *args, **options):
        """
        Execute the command.
        """
        dry_run = options['dry_run']
        retirements = UserRetirementStatus.objects.all().select_related('user')

        failed_retirements = []
        for retirement in retirements:
            original_username = retirement.original_username
            old_retired_username = retirement.retired_username
            new_retired_username = user_util.get_retired_username(
                original_username, settings.RETIRED_USER_SALTS,
                settings.RETIRED_USERNAME_FMT)

            # If the original username was already normalized (or all lowercase), the old and new hashes would
            # match:
            if old_retired_username == new_retired_username:
                print(
                    u'Skipping UserRetirementStatus ID {} / User ID {} because the hash would not change.'
                    .format(
                        retirement.id,
                        retirement.user.id,
                    ))
            # Found an username to update
            else:
                print(u'Updating UserRetirementStatus ID {} / User ID {} '
                      u'to rehash their retired username: {} -> {}'.format(
                          retirement.id, retirement.user.id,
                          old_retired_username, new_retired_username))
                if not dry_run:
                    try:
                        # Update the forums first, that way if it fails the user can
                        # be re-run. It does not need to be in the same transaction,
                        # as the local db updates and can be slow, so keeping it
                        # outside to cut down on potential deadlocks.
                        cc_user = comment_client.User.from_django_user(
                            retirement.user)

                        # The user may not exist in forums, if it doesn't that's not
                        # an error.
                        try:
                            cc_user.retire(new_retired_username)
                        except comment_client.utils.CommentClientRequestError as e:
                            if e.status_code != 404:
                                print(
                                    u'UserRetirementStatus ID {} User ID {} failed to rename in forums: {}'
                                    .format(retirement.id, retirement.user.id,
                                            text_type(e)))
                                raise

                        # Update and save both the user table and retirement queue table:
                        with transaction.atomic():
                            # Only rename them in auth_user if they've already been retired
                            if retirement.user.username == old_retired_username:
                                retirement.user.username = new_retired_username
                                retirement.user.save()
                            retirement.retired_username = new_retired_username
                            retirement.save()
                    except Exception as exc:  # pylint: disable=broad-except
                        print(
                            u'UserRetirementStatus ID {} User ID {} failed rename'
                            .format(retirement.id, retirement.user.id))
                        print(text_type(exc))
                        failed_retirements.append(retirement)

        if failed_retirements:
            print(
                '------------------------------------------------------------')
            print(
                u'FAILED! {} retirements failed to rehash. Retirement IDs:\n{}'
                .format(
                    len(failed_retirements),
                    '\n'.join([text_type(r.id) for r in failed_retirements])))
        else:
            print(u'Success! {} retirements examined.'.format(
                len(retirements)))
    def handle(self, *args, **options):
        """
        Execute the command.
        """
        dry_run = options['dry_run']
        retirements = UserRetirementStatus.objects.all().select_related('user')

        failed_retirements = []
        for retirement in retirements:
            original_username = retirement.original_username
            old_retired_username = retirement.retired_username
            new_retired_username = user_util.get_retired_username(
                original_username,
                settings.RETIRED_USER_SALTS,
                settings.RETIRED_USERNAME_FMT
            )

            # If the original username was already normalized (or all lowercase), the old and new hashes would
            # match:
            if old_retired_username == new_retired_username:
                print(
                    'Skipping UserRetirementStatus ID {} / User ID {} because the hash would not change.'.format(
                        retirement.id,
                        retirement.user.id,
                    )
                )
            # Found an username to update
            else:
                print(
                    'Updating UserRetirementStatus ID {} / User ID {} '
                    'to rehash their retired username: {} -> {}'.format(
                        retirement.id,
                        retirement.user.id,
                        old_retired_username,
                        new_retired_username
                    )
                )
                if not dry_run:
                    try:
                        # Update the forums first, that way if it fails the user can
                        # be re-run. It does not need to be in the same transaction,
                        # as the local db updates and can be slow, so keeping it
                        # outside to cut down on potential deadlocks.
                        cc_user = comment_client.User.from_django_user(retirement.user)

                        # The user may not exist in forums, if it doesn't that's not
                        # an error.
                        try:
                            cc_user.retire(new_retired_username)
                        except comment_client.utils.CommentClientRequestError as e:
                            if e.status_code != 404:
                                print(
                                    'UserRetirementStatus ID {} User ID {} failed to rename in forums: {}'.format(
                                        retirement.id, retirement.user.id, text_type(e)
                                    )
                                )
                                raise

                        # Update and save both the user table and retirement queue table:
                        with transaction.atomic():
                            # Only rename them in auth_user if they've already been retired
                            if retirement.user.username == old_retired_username:
                                retirement.user.username = new_retired_username
                                retirement.user.save()
                            retirement.retired_username = new_retired_username
                            retirement.save()
                    except Exception as exc:  # pylint: disable=broad-except
                        print(
                            'UserRetirementStatus ID {} User ID {} failed rename'.format(
                                retirement.id, retirement.user.id
                            )
                        )
                        print(text_type(exc))
                        failed_retirements.append(retirement)

        if failed_retirements:
            print('------------------------------------------------------------')
            print(
                'FAILED! {} retirements failed to rehash. Retirement IDs:\n{}'.format(
                    len(failed_retirements),
                    '\n'.join([text_type(r.id) for r in failed_retirements])
                )
            )
        else:
            print('Success! {} retirements examined.'.format(len(retirements)))
Пример #11
0
def test_username_to_hash_bad_salt(salt):
    """
    Salts that are *not* lists/tuples should fail.
    """
    with pytest.raises((ValueError, IndexError)):
        _ = user_util.get_retired_username('AnotherLearnerUserName', salt)