<commit_before># coding:utf-8
"""
Tests for student activation and login
"""


import datetime
import json
import unicodedata

import ddt
import six
from django.conf import settings
from django.contrib.auth.models import User
from django.core import mail
from django.core.cache import cache
from django.http import HttpResponse
from django.test.client import Client
from django.test.utils import override_settings
from django.urls import NoReverseMatch, reverse
from mock import patch
from six.moves import range

from openedx.core.djangoapps.password_policy.compliance import (
    NonCompliantPasswordException,
    NonCompliantPasswordWarning
)
from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, waffle
from openedx.core.djangoapps.user_api.accounts import EMAIL_MIN_LENGTH, EMAIL_MAX_LENGTH
from openedx.core.djangoapps.user_authn.cookies import jwt_cookies
from openedx.core.djangoapps.user_authn.views.login import (
    AllowedAuthUser,
    ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY
)
from openedx.core.djangoapps.user_authn.tests.utils import setup_login_oauth_client
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.core.lib.api.test_utils import ApiTestCase
from student.tests.factories import RegistrationFactory, UserFactory, UserProfileFactory
from util.password_policy_validators import DEFAULT_MAX_PASSWORD_LENGTH


@ddt.ddt
class LoginTest(SiteMixin, CacheIsolationTestCase):
    """
    Test login_user() view
    """

    ENABLED_CACHES = ['default']
    LOGIN_FAILED_WARNING = 'Email or password is incorrect'
    ACTIVATE_ACCOUNT_WARNING = 'In order to sign in, you need to activate your account'
    username = 'test'
    user_email = '<EMAIL>'
    password = '<PASSWORD>'

    def setUp(self):
        """Setup a test user along with its registration and profile"""
        super(LoginTest, self).setUp()
        self.user = self._create_user(self.username, self.user_email)

        RegistrationFactory(user=self.user)
        UserProfileFactory(user=self.user)

        self.client = Client()
        cache.clear()

        self.url = reverse('login_api')

    def _create_user(self, username, user_email):
        user = UserFactory.build(username=username, email=user_email)
        user.set_password(self.password)
        user.save()
        return user

    def test_login_success(self):
        response, mock_audit_log = self._login_response(
            self.user_email, self.password, patched_audit_log='student.models.AUDIT_LOG'
        )
        self._assert_response(response, success=True)
        self._assert_audit_log(mock_audit_log, 'info', [u'Login success', self.user_email])

    FEATURES_WITH_LOGIN_MFE_ENABLED = settings.FEATURES.copy()
    FEATURES_WITH_LOGIN_MFE_ENABLED['ENABLE_LOGIN_MICROFRONTEND'] = True

    @ddt.data(
        # Default redirect is dashboard.
        {
            'next_url': None,
            'course_id': None,
            'expected_redirect': '/homepage-placeholder',
        },
        # A relative path is an acceptable redirect.
        {
            'next_url': '/harmless-relative-page',
            'course_id': None,
            'expected_redirect': '/harmless-relative-page',
        },
        # Paths without trailing slashes are also considered relative.
        {
            'next_url': 'courses',
            'course_id': None,
            'expected_redirect': 'courses',
        },
        # An absolute URL to a non-whitelisted domain is not an acceptable redirect.
        {
            'next_url': 'https://evil.sketchysite',
            'course_id': None,
            'expected_redirect': '/homepage-placeholder',
        },
        # An absolute URL to a whitelisted domain is acceptable.
        {
            'next_url': 'https://openedx.service/coolpage',
            'course_id': None,
            'expected_redirect': 'https://openedx.service/coolpage',
        },
        # If course_id is provided, redirect to finish_auth with dashboard as next.
        {
            'next_url': None,
            'course_id': 'coursekey',
            'expected_redirect': (
                '/account/finish_auth?course_id=coursekey&next=%2Fhomepage-placeholder'
            ),
        },
        # If valid course_id AND next_url are provided, redirect to finish_auth with
        # provided next URL.
        {
            'next_url': 'freshpage',
            'course_id': 'coursekey',
            'expected_redirect': (
                '/account/finish_auth?course_id=coursekey&next=freshpage'
            )
        },
        # If course_id is provided with invalid next_url, redirect to finish_auth with
        # course_id and dashboard as next URL.
        {
            'next_url': 'http://scam.scam',
            'course_id': 'coursekey',
            'expected_redirect': (
                '/account/finish_auth?course_id=coursekey&next=%2Fhomepage-placeholder'
            ),
        },
    )
    @ddt.unpack
    @override_settings(LOGIN_REDIRECT_WHITELIST=['openedx.service'])
    @override_settings(FEATURES=FEATURES_WITH_LOGIN_MFE_ENABLED)
    @patch(
        # Mock out reverse for LMS and Studio default redirects
        # (/dashboard and /home, respectively).
        # That way, we can handle both cases, while also making it easier to change this
        # test if the default LMS redirect ever changes.
        'student.helpers.reverse',
        lambda urlname, *args, **kwargs: (
            '/homepage-placeholder' if urlname in {'dashboard', 'home'}
            else reverse(urlname)
        )
    )
    def test_login_success_with_redirect(self, next_url, course_id, expected_redirect):
        # Default redirect is student dashboard.
        post_params = {}
        if next_url:
            post_params['next'] = next_url
        if course_id:
            post_params['course_id'] = course_id
        response, _ = self._login_response(
            self.user_email,
            self.password,
            extra_post_params=post_params,
            HTTP_ACCEPT='*/*',
        )
        self._assert_response(response, success=True)
        self._assert_redirect_url(response, expected_redirect)

    @patch.dict("django.conf.settings.FEATURES", {'SQUELCH_PII_IN_LOGS': True})
    def test_login_success_no_pii(self):<commit_msg>fixup! fixup! Handle next and course_id in /login_ajax<commit_after># coding:utf-8
"""
Tests for student activation and login
"""


import datetime
import json
import unicodedata

import ddt
import six
from django.conf import settings
from django.contrib.auth.models import User
from django.core import mail
from django.core.cache import cache
from django.http import HttpResponse
from django.test.client import Client
from django.test.utils import override_settings
from django.urls import NoReverseMatch, reverse
from mock import patch
from six.moves import range

from openedx.core.djangoapps.password_policy.compliance import (
    NonCompliantPasswordException,
    NonCompliantPasswordWarning
)
from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, waffle
from openedx.core.djangoapps.user_api.accounts import EMAIL_MIN_LENGTH, EMAIL_MAX_LENGTH
from openedx.core.djangoapps.user_authn.cookies import jwt_cookies
from openedx.core.djangoapps.user_authn.views.login import (
    AllowedAuthUser,
    ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY
)
from openedx.core.djangoapps.user_authn.tests.utils import setup_login_oauth_client
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.core.lib.api.test_utils import ApiTestCase
from student.tests.factories import RegistrationFactory, UserFactory, UserProfileFactory
from util.password_policy_validators import DEFAULT_MAX_PASSWORD_LENGTH


@ddt.ddt
class LoginTest(SiteMixin, CacheIsolationTestCase):
    """
    Test login_user() view
    """

    ENABLED_CACHES = ['default']
    LOGIN_FAILED_WARNING = 'Email or password is incorrect'
    ACTIVATE_ACCOUNT_WARNING = 'In order to sign in, you need to activate your account'
    username = 'test'
    user_email = '<EMAIL>'
    password = '<PASSWORD>'

    def setUp(self):
        """Setup a test user along with its registration and profile"""
        super(LoginTest, self).setUp()
        self.user = self._create_user(self.username, self.user_email)

        RegistrationFactory(user=self.user)
        UserProfileFactory(user=self.user)

        self.client = Client()
        cache.clear()

        self.url = reverse('login_api')

    def _create_user(self, username, user_email):
        user = UserFactory.build(username=username, email=user_email)
        user.set_password(<PASSWORD>)
        user.save()
        return user

    def test_login_success(self):
        response, mock_audit_log = self._login_response(
            self.user_email, self.password, patched_audit_log='student.models.AUDIT_LOG'
        )
        self._assert_response(response, success=True)
        self._assert_audit_log(mock_audit_log, 'info', [u'Login success', self.user_email])

    FEATURES_WITH_LOGIN_MFE_ENABLED = settings.FEATURES.copy()
    FEATURES_WITH_LOGIN_MFE_ENABLED['ENABLE_LOGIN_MICROFRONTEND'] = True

    @ddt.data(
        # Default redirect is dashboard.
        {
            'next_url': None,
            'course_id': None,
            'expected_redirect': '/dashboard',
        },
        # A relative path is an acceptable redirect.
        {
            'next_url': '/harmless-relative-page',
            'course_id': None,
            'expected_redirect': '/harmless-relative-page',
        },
        # Paths without trailing slashes are also considered relative.
        {
            'next_url': 'courses',
            'course_id': None,
            'expected_redirect': 'courses',
        },
        # An absolute URL to a non-whitelisted domain is not an acceptable redirect.
        {
            'next_url': 'https://evil.sketchysite',
            'course_id': None,
            'expected_redirect': '/dashboard',
        },
        # An absolute URL to a whitelisted domain is acceptable.
        {
            'next_url': 'https://openedx.service/coolpage',
            'course_id': None,
            'expected_redirect': 'https://openedx.service/coolpage',
        },
        # If course_id is provided, redirect to finish_auth with dashboard as next.
        {
            'next_url': None,
            'course_id': 'coursekey',
            'expected_redirect': (
                '/account/finish_auth?course_id=coursekey&next=%2Fdashboard'
            ),
        },
        # If valid course_id AND next_url are provided, redirect to finish_auth with
        # provided next URL.
        {
            'next_url': 'freshpage',
            'course_id': 'coursekey',
            'expected_redirect': (
                '/account/finish_auth?course_id=coursekey&next=freshpage'
            )
        },
        # If course_id is provided with invalid next_url, redirect to finish_auth with
        # course_id and dashboard as next URL.
        {
            'next_url': 'http://scam.scam',
            'course_id': 'coursekey',
            'expected_redirect': (
                '/account/finish_auth?course_id=coursekey&next=%2Fdashboard'
            ),
        },
    )
    @ddt.unpack
    @override_settings(LOGIN_REDIRECT_WHITELIST=['openedx.service'])
    @override_settings(FEATURES=FEATURES_WITH_LOGIN_MFE_ENABLED)
    @skip_unless_lms
    def test_login_success_with_redirect(self, next_url, course_id, expected_redirect):
        post_params = {}
        if next_url:
            post_params['next'] = next_url
        if course_id:
            post_params['course_id'] = course_id
        response, _ = self._login_response(
            self.user_email,
            self.password,
            extra_post_params=post_params,
            HTTP_ACCEPT='*/*',
        )
        self._assert_response(response, success=True)
        self._assert_redirect_url(response, expected_redirect)

    @patch.dict("django.conf.settings.FEATURES", {'SQUELCH_PII_IN_LOGS': True})
    def test_login_success_no_pii(self):
{'fraction_non_alphanumeric': 0.09209884571614371, 'fraction_numerical': 0.00048772557307754836, 'mean_word_length': 2.8101579436358004, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '372395', 'n_tokens_mistral': 3607, 'n_tokens_neox': 3561, 'n_words': 782}