From c1d4458b836aef9bd54ac23d7cfdc3bec38be38d Mon Sep 17 00:00:00 2001 From: Brittney Exline Date: Wed, 10 Jan 2018 11:33:24 -0500 Subject: [PATCH] ENT-838 Management command to change an enterprise user's username --- .../change_enterprise_user_username.py | 61 +++++++++++++++++ .../test_change_enterprise_user_username.py | 68 +++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 common/djangoapps/student/management/commands/change_enterprise_user_username.py create mode 100644 common/djangoapps/student/management/tests/test_change_enterprise_user_username.py diff --git a/common/djangoapps/student/management/commands/change_enterprise_user_username.py b/common/djangoapps/student/management/commands/change_enterprise_user_username.py new file mode 100644 index 0000000000..b2fbfa8a14 --- /dev/null +++ b/common/djangoapps/student/management/commands/change_enterprise_user_username.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +""" +Django management command for changing an enterprise user's username. +""" +from __future__ import absolute_import, unicode_literals + +import logging + +from django.contrib.auth.models import User +from django.core.management import BaseCommand + +from enterprise.models import EnterpriseCustomerUser + +LOGGER = logging.getLogger(__name__) + + +class Command(BaseCommand): + """ + Updates the username value for a given user. + + This is NOT MEANT for general use, and is specifically limited to Enterprise Users since + only they could potentially experience the issue of overwritten usernames. + + See ENT-832 for details on the bug that modified usernames for some Enterprise Users. + """ + help = 'Update the username of a given user.' + + def add_arguments(self, parser): + parser.add_argument( + '-u', + '--user_id', + action='store', + dest='user_id', + default=None, + help='The ID of the user to update.' + ) + + parser.add_argument( + '-n', + '--new_username', + action='store', + dest='new_username', + default=None, + help='The username value to set for the user.' + ) + + def handle(self, *args, **options): + user_id = options.get('user_id') + new_username = options.get('new_username') + + try: + EnterpriseCustomerUser.objects.get(user_id=user_id) + except EnterpriseCustomerUser.DoesNotExist: + LOGGER.info('User {} must be an Enterprise User.'.format(user_id)) + return + + user = User.objects.get(id=user_id) + user.username = new_username + user.save() + + LOGGER.info('User {} has been updated with username {}.'.format(user_id, new_username)) diff --git a/common/djangoapps/student/management/tests/test_change_enterprise_user_username.py b/common/djangoapps/student/management/tests/test_change_enterprise_user_username.py new file mode 100644 index 0000000000..c7fddf4857 --- /dev/null +++ b/common/djangoapps/student/management/tests/test_change_enterprise_user_username.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +""" +Tests for the django management command `change_enterprise_user_username`. +""" +from __future__ import absolute_import, unicode_literals + +import mock +from pytest import mark + +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core.management import call_command +from django.db.models.signals import post_save +from django.test import TestCase + +from enterprise.models import EnterpriseCustomer, EnterpriseCustomerUser + + +@mark.django_db +class ChangeEnterpriseUserUsernameCommandTests(TestCase): + """ + Test command `change_enterprise_user_username`. + """ + command = 'change_enterprise_user_username' + + @mock.patch('student.management.commands.change_enterprise_user_username.LOGGER') + def test_user_not_enterprise(self, logger_mock): + """ + Test that the command does not update a user's username if it is not linked to an Enterprise. + """ + user = User.objects.create(is_active=True, username='old_username', email='test@example.com') + new_username = 'new_username' + + post_save_handler = mock.MagicMock() + post_save.connect(post_save_handler, sender=User) + + call_command(self.command, user_id=user.id, new_username=new_username) + + logger_mock.info.assert_called_with('User {} must be an Enterprise User.'.format(user.id)) + post_save_handler.assert_not_called() + + @mock.patch('student.management.commands.change_enterprise_user_username.LOGGER') + def test_username_updated_successfully(self, logger_mock): + """ + Test that the command updates the user's username when the user is linked to an Enterprise. + """ + user = User.objects.create(is_active=True, username='old_username', email='test@example.com') + site = Site.objects.create(domain='example.com') + enterprise_customer = EnterpriseCustomer.objects.create( + name='Test EnterpriseCustomer', + site=site + ) + EnterpriseCustomerUser.objects.create( + user_id=user.id, + enterprise_customer=enterprise_customer + ) + new_username = 'new_username' + + post_save_handler = mock.MagicMock() + post_save.connect(post_save_handler, sender=User) + + call_command(self.command, user_id=user.id, new_username=new_username) + + logger_mock.info.assert_called_with('User {} has been updated with username {}.'.format(user.id, new_username)) + post_save_handler.assert_called() + + updated_user = User.objects.get(id=user.id) + assert updated_user.username == new_username