<commit_before>import logging
import os
import re
from enum import Enum
from typing import Any, Dict, Mapping, Optional, Tuple

from flask import Response as FlaskResponse
from flask import make_response
from mock import patch
from saml2 import BINDING_HTTP_REDIRECT
from saml2.client import Saml2Client

from eduid_common.api.app import EduIDBaseApp
from eduid_common.authn.utils import get_saml2_config
from vccs_client import VCCSClient

from eduid_webapp.idp.settings.common import IdPConfig
from eduid_webapp.idp.tests.test_app import IdPTests

logger = logging.getLogger(__name__)

HERE = os.path.abspath(os.path.dirname(__file__))


class LoginState(Enum):
    S0_REDIRECT = 'redirect'
    S1_LOGIN_FORM = 'login-form'
    S2_VERIFY = 'verify'
    S3_REDIRECT_LOGGED_IN = 'redirect-logged-in'
    S4_REDIRECT_TO_ACS = 'redirect-to-acs'
    S5_LOGGED_IN = 'logged-in'


class IdPTestApp(EduIDBaseApp):
    def __init__(self, name: str, config: Dict[str, Any], **kwargs):
        self.config = IdPConfig.init_config(ns='webapp', app_name=name, test_config=config)
        super().__init__(name, **kwargs)


class IdPAPITestBase(IdPTests):
    def setUp(self):
        super().setUp()
        self.idp_entity_id = 'https://unittest-idp.example.edu/idp.xml'
        self.relay_state = 'test-fest'
        self.sp_config = get_saml2_config(self.app.config.pysaml2_config, name='SP_CONFIG')
        self.saml2_client = Saml2Client(config=self.sp_config)

    def update_config(self, config):
        config = super().update_config(config)
        config.update({'signup_link': 'TEST-SIGNUP-LINK', 'log_level': 'DEBUG'})
        return config

    def test_display_of_login_page(self):
        next_url = '/back-to-test-marker'

        (session_id, info) = self.saml2_client.prepare_for_authenticate(
            entityid=self.idp_entity_id, relay_state=next_url, binding=BINDING_HTTP_REDIRECT,
        )

        path = self._extract_path_from_info(info)

        response = self.browser.get(path)
        body = response.data.decode('utf-8')

        assert response.status_code == 200
        assert self.app.config.signup_link in body

        # the RelayState is present as a hidden form parameter in the login page
        assert next_url in body

    def test_submitting_wrong_credentials(self):
        reached_state, resp = self._try_login()

        assert reached_state == LoginState.S2_VERIFY

        # check that we were sent back to the login screen
        redirect_loc = self._extract_path_from_response(resp)
        assert redirect_loc.startswith('/sso/redirect?SAMLRequest=')

    def test_successful_authentication(self):
        # Patch the VCCSClient so we do not need a vccs server
        with patch.object(VCCSClient, 'authenticate'):
            VCCSClient.authenticate.return_value = True
            reached_state, resp = self._try_login()

        assert reached_state == LoginState.S5_LOGGED_IN

    def test_terminated_user(self):
        user = self.amdb.get_user_by_eppn(self.test_user.eppn)
        user.terminated = True
        self.amdb.save(user)

        # Patch the VCCSClient so we do not need a vccs server
        with patch.object(VCCSClient, 'authenticate'):
            VCCSClient.authenticate.return_value = True
            reached_state, resp = self._try_login()

        assert reached_state == LoginState.S5_LOGGED_IN

    def test_with_unknown_sp(self):
        sp_config = get_saml2_config(self.app.config.pysaml2_config, name='UNKNOWN_SP_CONFIG')
        saml2_client = Saml2Client(config=sp_config)

        # Patch the VCCSClient so we do not need a vccs server
        with patch.object(VCCSClient, 'authenticate'):
            VCCSClient.authenticate.return_value = True
            reached_state, resp = self._try_login(saml2_client=saml2_client)

        assert reached_state == LoginState.S3_REDIRECT_LOGGED_IN
        assert b'SAML error: Unknown Service Provider' in resp.data

    def test_sso_to_unknown_sp(self):
        # Patch the VCCSClient so we do not need a vccs server
        with patch.object(VCCSClient, 'authenticate'):
            VCCSClient.authenticate.return_value = True
            reached_state, resp = self._try_login()

        assert reached_state == LoginState.S5_LOGGED_IN

        sp_config = get_saml2_config(self.app.config.pysaml2_config, name='UNKNOWN_SP_CONFIG')
        saml2_client = Saml2Client(config=sp_config)

        # Don't patch VCCS here to ensure a SSO is done, not a password authentication
        reached_state, resp = self._try_login(saml2_client=saml2_client)

        assert reached_state == LoginState.S0_REDIRECT
        assert b'SAML error: Unknown Service Provider' in resp.data<commit_msg>check for removed cookie (does not work yet)<commit_after>import logging
import os
import re
from enum import Enum
from typing import Any, Dict, Mapping, Optional, Tuple

from flask import Response as FlaskResponse
from flask import make_response
from mock import patch
from saml2 import BINDING_HTTP_REDIRECT
from saml2.client import Saml2Client

from eduid_common.api.app import EduIDBaseApp
from eduid_common.authn.utils import get_saml2_config
from vccs_client import VCCSClient

from eduid_webapp.idp.settings.common import IdPConfig
from eduid_webapp.idp.tests.test_app import IdPTests

logger = logging.getLogger(__name__)

HERE = os.path.abspath(os.path.dirname(__file__))


class LoginState(Enum):
    S0_REDIRECT = 'redirect'
    S1_LOGIN_FORM = 'login-form'
    S2_VERIFY = 'verify'
    S3_REDIRECT_LOGGED_IN = 'redirect-logged-in'
    S4_REDIRECT_TO_ACS = 'redirect-to-acs'
    S5_LOGGED_IN = 'logged-in'


class IdPTestApp(EduIDBaseApp):
    def __init__(self, name: str, config: Dict[str, Any], **kwargs):
        self.config = IdPConfig.init_config(ns='webapp', app_name=name, test_config=config)
        super().__init__(name, **kwargs)


class IdPAPITestBase(IdPTests):
    def setUp(self):
        super().setUp()
        self.idp_entity_id = 'https://unittest-idp.example.edu/idp.xml'
        self.relay_state = 'test-fest'
        self.sp_config = get_saml2_config(self.app.config.pysaml2_config, name='SP_CONFIG')
        self.saml2_client = Saml2Client(config=self.sp_config)

    def update_config(self, config):
        config = super().update_config(config)
        config.update({'signup_link': 'TEST-SIGNUP-LINK', 'log_level': 'DEBUG'})
        return config

    def test_display_of_login_page(self):
        next_url = '/back-to-test-marker'

        (session_id, info) = self.saml2_client.prepare_for_authenticate(
            entityid=self.idp_entity_id, relay_state=next_url, binding=BINDING_HTTP_REDIRECT,
        )

        path = self._extract_path_from_info(info)

        response = self.browser.get(path)
        body = response.data.decode('utf-8')

        assert response.status_code == 200
        assert self.app.config.signup_link in body

        # the RelayState is present as a hidden form parameter in the login page
        assert next_url in body

    def test_submitting_wrong_credentials(self):
        reached_state, resp = self._try_login()

        assert reached_state == LoginState.S2_VERIFY

        # check that we were sent back to the login screen
        redirect_loc = self._extract_path_from_response(resp)
        assert redirect_loc.startswith('/sso/redirect?SAMLRequest=')

    def test_successful_authentication(self):
        # Patch the VCCSClient so we do not need a vccs server
        with patch.object(VCCSClient, 'authenticate'):
            VCCSClient.authenticate.return_value = True
            reached_state, resp = self._try_login()

        assert reached_state == LoginState.S5_LOGGED_IN

    def test_terminated_user(self):
        user = self.amdb.get_user_by_eppn(self.test_user.eppn)
        user.terminated = True
        self.amdb.save(user)

        # Patch the VCCSClient so we do not need a vccs server
        with patch.object(VCCSClient, 'authenticate'):
            VCCSClient.authenticate.return_value = True
            reached_state, resp = self._try_login()

        assert reached_state == LoginState.S5_LOGGED_IN
        cookies = resp.headers['Set-Cookie']
        # Ensure the idpauthn cookie is removed for terminated users
        assert 'idpauthn' in cookies

    def test_with_unknown_sp(self):
        sp_config = get_saml2_config(self.app.config.pysaml2_config, name='UNKNOWN_SP_CONFIG')
        saml2_client = Saml2Client(config=sp_config)

        # Patch the VCCSClient so we do not need a vccs server
        with patch.object(VCCSClient, 'authenticate'):
            VCCSClient.authenticate.return_value = True
            reached_state, resp = self._try_login(saml2_client=saml2_client)

        assert reached_state == LoginState.S3_REDIRECT_LOGGED_IN
        assert b'SAML error: Unknown Service Provider' in resp.data

    def test_sso_to_unknown_sp(self):
        # Patch the VCCSClient so we do not need a vccs server
        with patch.object(VCCSClient, 'authenticate'):
            VCCSClient.authenticate.return_value = True
            reached_state, resp = self._try_login()

        assert reached_state == LoginState.S5_LOGGED_IN

        sp_config = get_saml2_config(self.app.config.pysaml2_config, name='UNKNOWN_SP_CONFIG')
        saml2_client = Saml2Client(config=sp_config)

        # Don't patch VCCS here to ensure a SSO is done, not a password authentication
        reached_state, resp = self._try_login(saml2_client=saml2_client)

        assert reached_state == LoginState.S0_REDIRECT
        assert b'SAML error: Unknown Service Provider' in resp.data
{'fraction_non_alphanumeric': 0.08270833333333333, 'fraction_numerical': 0.0077083333333333335, 'mean_word_length': 3.3502492070684187, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, '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': '399360', 'n_tokens_mistral': 3165, 'n_tokens_neox': 3088, 'n_words': 677}