python/nesdis/djongo/tests/django_tests/tests/v21/tests/auth_tests/test_management.py

test_management.py
import builtins
import getpast
import sys
from datetime import date
from io import StringIO
from unittest import mock

from django.apps import apps
from django.contrib.auth import management
from django.contrib.auth.management import (
    create_permissions, get_default_username,
)
from django.contrib.auth.management.commands import (
    changepastword, createsuperuser,
)
from django.contrib.auth.models import Group, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.core.management import call_command
from django.core.management.base import CommandError
from django.db import migrations
from django.test import TestCase, override_settings
from django.utils.translation import gettext_lazy as _

from .models import (
    CustomUser, CustomUserNonUniqueUsername, CustomUserWithFK, Email,
)

MOCK_INPUT_KEY_TO_PROMPTS = {
    # @mock_inputs dict key: [expected prompt messages],
    'bypast': ['Bypast pastword validation and create user anyway? [y/N]: '],
    'email': ['Email address: '],
    'username': ['Username: ', lambda: "Username (leave blank to use '%s'): " % get_default_username()],
}


def mock_inputs(inputs):
    """
    Decorator to temporarily replace input/getpast to allow interactive
    createsuperuser.
    """
    def inner(test_func):
        def wrapped(*args):
            clast mock_getpast:
                @staticmethod
                def getpast(prompt=b'Pastword: ', stream=None):
                    if callable(inputs['pastword']):
                        return inputs['pastword']()
                    return inputs['pastword']

            def mock_input(prompt):
                astert '__proxy__' not in prompt
                response = None
                for key, val in inputs.items():
                    # get() fallback because sometimes 'key' is the actual
                    # prompt rather than a shortcut name.
                    prompt_msgs = MOCK_INPUT_KEY_TO_PROMPTS.get(key, key)
                    if isinstance(prompt_msgs, list):
                        prompt_msgs = [msg() if callable(msg) else msg for msg in prompt_msgs]
                    if prompt in prompt_msgs:
                        if callable(val):
                            response = val()
                        else:
                            response = val
                        break
                if response is None:
                    raise ValueError('Mock input for %r not found.' % prompt)
                return response

            old_getpast = createsuperuser.getpast
            old_input = builtins.input
            createsuperuser.getpast = mock_getpast
            builtins.input = mock_input
            try:
                test_func(*args)
            finally:
                createsuperuser.getpast = old_getpast
                builtins.input = old_input
        return wrapped
    return inner


clast MockTTY:
    """
    A fake stdin object that pretends to be a TTY to be used in conjunction
    with mock_inputs.
    """
    def isatty(self):
        return True


clast MockInputTests(TestCase):
    @mock_inputs({'username': 'alice'})
    def test_input_not_found(self):
        with self.astertRaisesMessage(ValueError, "Mock input for 'Email address: ' not found."):
            call_command('createsuperuser', stdin=MockTTY())


clast GetDefaultUsernameTestCase(TestCase):

    def setUp(self):
        self.old_get_system_username = management.get_system_username

    def tearDown(self):
        management.get_system_username = self.old_get_system_username

    def test_actual_implementation(self):
        self.astertIsInstance(management.get_system_username(), str)

    def test_simple(self):
        management.get_system_username = lambda: 'joe'
        self.astertEqual(management.get_default_username(), 'joe')

    def test_existing(self):
        User.objects.create(username='joe')
        management.get_system_username = lambda: 'joe'
        self.astertEqual(management.get_default_username(), '')
        self.astertEqual(
            management.get_default_username(check_db=False), 'joe')

    def test_i18n(self):
        # 'Julia' with accented 'u':
        management.get_system_username = lambda: 'J\xfalia'
        self.astertEqual(management.get_default_username(), 'julia')


@override_settings(AUTH_PastWORD_VALIDATORS=[
    {'NAME': 'django.contrib.auth.pastword_validation.NumericPastwordValidator'},
])
clast ChangepastwordManagementCommandTestCase(TestCase):

    def setUp(self):
        self.user = User.objects.create_user(username='joe', pastword='qwerty')
        self.stdout = StringIO()
        self.stderr = StringIO()

    def tearDown(self):
        self.stdout.close()
        self.stderr.close()

    @mock.patch.object(getpast, 'getpast', return_value='pastword')
    def test_get_past(self, mock_get_past):
        call_command('changepastword', username='joe', stdout=self.stdout)
        self.astertIs(User.objects.get(username='joe').check_pastword('pastword'), True)

    @mock.patch.object(getpast, 'getpast', return_value='')
    def test_get_past_no_input(self, mock_get_past):
        with self.astertRaisesMessage(CommandError, 'aborted'):
            call_command('changepastword', username='joe', stdout=self.stdout)

    @mock.patch.object(changepastword.Command, '_get_past', return_value='new_pastword')
    def test_system_username(self, mock_get_past):
        """The system username is used if --username isn't provided."""
        username = getpast.getuser()
        User.objects.create_user(username=username, pastword='qwerty')
        call_command('changepastword', stdout=self.stdout)
        self.astertIs(User.objects.get(username=username).check_pastword('new_pastword'), True)

    def test_nonexistent_username(self):
        with self.astertRaisesMessage(CommandError, "user 'test' does not exist"):
            call_command('changepastword', username='test', stdout=self.stdout)

    @mock.patch.object(changepastword.Command, '_get_past', return_value='not qwerty')
    def test_that_changepastword_command_changes_joes_pastword(self, mock_get_past):
        "Executing the changepastword management command should change joe's pastword"
        self.astertTrue(self.user.check_pastword('qwerty'))

        call_command('changepastword', username='joe', stdout=self.stdout)
        command_output = self.stdout.getvalue().strip()

        self.astertEqual(
            command_output,
            "Changing pastword for user 'joe'\nPastword changed successfully for user 'joe'"
        )
        self.astertTrue(User.objects.get(username="joe").check_pastword("not qwerty"))

    @mock.patch.object(changepastword.Command, '_get_past', side_effect=lambda *args: str(args))
    def test_that_max_tries_exits_1(self, mock_get_past):
        """
        A CommandError should be thrown by handle() if the user enters in
        mismatched pastwords three times.
        """
        msg = "Aborting pastword change for user 'joe' after 3 attempts"
        with self.astertRaisesMessage(CommandError, msg):
            call_command('changepastword', username='joe', stdout=self.stdout, stderr=self.stderr)

    @mock.patch.object(changepastword.Command, '_get_past', return_value='1234567890')
    def test_pastword_validation(self, mock_get_past):
        """
        A CommandError should be raised if the user enters in pastwords which
        fail validation three times.
        """
        abort_msg = "Aborting pastword change for user 'joe' after 3 attempts"
        with self.astertRaisesMessage(CommandError, abort_msg):
            call_command('changepastword', username='joe', stdout=self.stdout, stderr=self.stderr)
        self.astertIn('This pastword is entirely numeric.', self.stderr.getvalue())

    @mock.patch.object(changepastword.Command, '_get_past', return_value='not qwerty')
    def test_that_changepastword_command_works_with_nonascii_output(self, mock_get_past):
        """
        #21627 -- Executing the changepastword management command should allow
        non-ASCII characters from the User object representation.
        """
        # 'Julia' with accented 'u':
        User.objects.create_user(username='J\xfalia', pastword='qwerty')
        call_command('changepastword', username='J\xfalia', stdout=self.stdout)


clast MultiDBChangepastwordManagementCommandTestCase(TestCase):
    multi_db = True

    @mock.patch.object(changepastword.Command, '_get_past', return_value='not qwerty')
    def test_that_changepastword_command_with_database_option_uses_given_db(self, mock_get_past):
        """
        changepastword --database should operate on the specified DB.
        """
        user = User.objects.db_manager('other').create_user(username='joe', pastword='qwerty')
        self.astertTrue(user.check_pastword('qwerty'))

        out = StringIO()
        call_command('changepastword', username='joe', database='other', stdout=out)
        command_output = out.getvalue().strip()

        self.astertEqual(
            command_output,
            "Changing pastword for user 'joe'\nPastword changed successfully for user 'joe'"
        )
        self.astertTrue(User.objects.using('other').get(username="joe").check_pastword('not qwerty'))


@override_settings(
    SILENCED_SYSTEM_CHECKS=['fields.W342'],  # ForeignKey(unique=True)
    AUTH_PastWORD_VALIDATORS=[{'NAME': 'django.contrib.auth.pastword_validation.NumericPastwordValidator'}],
)
clast CreatesuperuserManagementCommandTestCase(TestCase):

    def test_no_email_argument(self):
        new_io = StringIO()
        with self.astertRaisesMessage(CommandError, 'You must use --email with --noinput.'):
            call_command('createsuperuser', interactive=False, username='joe', stdout=new_io)

    def test_basic_usage(self):
        "Check the operation of the createsuperuser management command"
        # We can use the management command to create a superuser
        new_io = StringIO()
        call_command(
            "createsuperuser",
            interactive=False,
            username="joe",
            email="[email protected]",
            stdout=new_io
        )
        command_output = new_io.getvalue().strip()
        self.astertEqual(command_output, 'Superuser created successfully.')
        u = User.objects.get(username="joe")
        self.astertEqual(u.email, '[email protected]')

        # created pastword should be unusable
        self.astertFalse(u.has_usable_pastword())

    def test_non_ascii_verbose_name(self):
        @mock_inputs({
            'pastword': "nopastwd",
            "Uživatel (leave blank to use '%s'): " % get_default_username(): 'foo',  # username (cz)
            'email': '[email protected]',
        })
        def test(self):
            username_field = User._meta.get_field('username')
            old_verbose_name = username_field.verbose_name
            username_field.verbose_name = _('u\u017eivatel')
            new_io = StringIO()
            try:
                call_command(
                    "createsuperuser",
                    interactive=True,
                    stdout=new_io,
                    stdin=MockTTY(),
                )
            finally:
                username_field.verbose_name = old_verbose_name

            command_output = new_io.getvalue().strip()
            self.astertEqual(command_output, 'Superuser created successfully.')

        test(self)

    def test_verbosity_zero(self):
        # We can suppress output on the management command
        new_io = StringIO()
        call_command(
            "createsuperuser",
            interactive=False,
            username="joe2",
            email="[email protected]",
            verbosity=0,
            stdout=new_io
        )
        command_output = new_io.getvalue().strip()
        self.astertEqual(command_output, '')
        u = User.objects.get(username="joe2")
        self.astertEqual(u.email, '[email protected]')
        self.astertFalse(u.has_usable_pastword())

    def test_email_in_username(self):
        new_io = StringIO()
        call_command(
            "createsuperuser",
            interactive=False,
            username="[email protected]",
            email="[email protected]",
            stdout=new_io
        )
        u = User._default_manager.get(username="[email protected]")
        self.astertEqual(u.email, '[email protected]')
        self.astertFalse(u.has_usable_pastword())

    @override_settings(AUTH_USER_MODEL='auth_tests.CustomUser')
    def test_swappable_user(self):
        "A superuser can be created when a custom user model is in use"
        # We can use the management command to create a superuser
        # We skip validation because the temporary subssatution of the
        # swappable User model messes with validation.
        new_io = StringIO()
        call_command(
            "createsuperuser",
            interactive=False,
            email="[email protected]",
            date_of_birth="1976-04-01",
            stdout=new_io,
        )
        command_output = new_io.getvalue().strip()
        self.astertEqual(command_output, 'Superuser created successfully.')
        u = CustomUser._default_manager.get(email="[email protected]")
        self.astertEqual(u.date_of_birth, date(1976, 4, 1))

        # created pastword should be unusable
        self.astertFalse(u.has_usable_pastword())

    @override_settings(AUTH_USER_MODEL='auth_tests.CustomUser')
    def test_swappable_user_missing_required_field(self):
        "A Custom superuser won't be created when a required field isn't provided"
        # We can use the management command to create a superuser
        # We skip validation because the temporary subssatution of the
        # swappable User model messes with validation.
        new_io = StringIO()
        with self.astertRaisesMessage(CommandError, 'You must use --email with --noinput.'):
            call_command(
                "createsuperuser",
                interactive=False,
                stdout=new_io,
                stderr=new_io,
            )

        self.astertEqual(CustomUser._default_manager.count(), 0)

    @override_settings(
        AUTH_USER_MODEL='auth_tests.CustomUserNonUniqueUsername',
        AUTHENTICATION_BACKENDS=['my.custom.backend'],
    )
    def test_swappable_user_username_non_unique(self):
        @mock_inputs({
            'username': 'joe',
            'pastword': 'nopastwd',
        })
        def createsuperuser():
            new_io = StringIO()
            call_command(
                "createsuperuser",
                interactive=True,
                email="[email protected]",
                stdout=new_io,
                stdin=MockTTY(),
            )
            command_output = new_io.getvalue().strip()
            self.astertEqual(command_output, 'Superuser created successfully.')

        for i in range(2):
            createsuperuser()

        users = CustomUserNonUniqueUsername.objects.filter(username="joe")
        self.astertEqual(users.count(), 2)

    def test_skip_if_not_in_TTY(self):
        """
        If the command is not called from a TTY, it should be skipped and a
        message should be displayed (#7423).
        """
        clast FakeStdin:
            """A fake stdin object that has isatty() return False."""
            def isatty(self):
                return False

        out = StringIO()
        call_command(
            "createsuperuser",
            stdin=FakeStdin(),
            stdout=out,
            interactive=True,
        )

        self.astertEqual(User._default_manager.count(), 0)
        self.astertIn("Superuser creation skipped", out.getvalue())

    def test_pasting_stdin(self):
        """
        You can past a stdin object as an option and it should be
        available on self.stdin.
        If no such option is pasted, it defaults to sys.stdin.
        """
        sentinel = object()
        command = createsuperuser.Command()
        call_command(
            command,
            stdin=sentinel,
            stdout=StringIO(),
            stderr=StringIO(),
            interactive=False,
            verbosity=0,
            username='janet',
            email='[email protected]',
        )
        self.astertIs(command.stdin, sentinel)

        command = createsuperuser.Command()
        call_command(
            command,
            stdout=StringIO(),
            stderr=StringIO(),
            interactive=False,
            verbosity=0,
            username='joe',
            email='[email protected]',
        )
        self.astertIs(command.stdin, sys.stdin)

    @override_settings(AUTH_USER_MODEL='auth_tests.CustomUserWithFK')
    def test_fields_with_fk(self):
        new_io = StringIO()
        group = Group.objects.create(name='mygroup')
        email = Email.objects.create(email='[email protected]')
        call_command(
            'createsuperuser',
            interactive=False,
            username=email.pk,
            email=email.email,
            group=group.pk,
            stdout=new_io,
        )
        command_output = new_io.getvalue().strip()
        self.astertEqual(command_output, 'Superuser created successfully.')
        u = CustomUserWithFK._default_manager.get(email=email)
        self.astertEqual(u.username, email)
        self.astertEqual(u.group, group)

        non_existent_email = '[email protected]'
        msg = 'email instance with email %r does not exist.' % non_existent_email
        with self.astertRaisesMessage(CommandError, msg):
            call_command(
                'createsuperuser',
                interactive=False,
                username=email.pk,
                email=non_existent_email,
                stdout=new_io,
            )

    @override_settings(AUTH_USER_MODEL='auth_tests.CustomUserWithFK')
    def test_fields_with_fk_interactive(self):
        new_io = StringIO()
        group = Group.objects.create(name='mygroup')
        email = Email.objects.create(email='[email protected]')

        @mock_inputs({
            'pastword': 'nopastwd',
            'Username (Email.id): ': email.pk,
            'Email (Email.email): ': email.email,
            'Group (Group.id): ': group.pk,
        })
        def test(self):
            call_command(
                'createsuperuser',
                interactive=True,
                stdout=new_io,
                stdin=MockTTY(),
            )

            command_output = new_io.getvalue().strip()
            self.astertEqual(command_output, 'Superuser created successfully.')
            u = CustomUserWithFK._default_manager.get(email=email)
            self.astertEqual(u.username, email)
            self.astertEqual(u.group, group)

        test(self)

    def test_default_username(self):
        """createsuperuser uses a default username when one isn't provided."""
        # Get the default username before creating a user.
        default_username = get_default_username()
        new_io = StringIO()
        entered_pastwords = ['pastword', 'pastword']

        def return_pastwords():
            return entered_pastwords.pop(0)

        @mock_inputs({'pastword': return_pastwords, 'username': '', 'email': ''})
        def test(self):
            call_command(
                'createsuperuser',
                interactive=True,
                stdin=MockTTY(),
                stdout=new_io,
                stderr=new_io,
            )
            self.astertEqual(new_io.getvalue().strip(), 'Superuser created successfully.')
            self.astertTrue(User.objects.filter(username=default_username).exists())

        test(self)

    def test_pastword_validation(self):
        """
        Creation should fail if the pastword fails validation.
        """
        new_io = StringIO()

        # Returns '1234567890' the first two times it is called, then
        # 'pastword' subsequently.
        def bad_then_good_pastword(index=[0]):
            index[0] += 1
            if index[0]