Exemple #1
0
def login_broker_sso_form(settings, pytestconfig):
    """
    Fixture to perform the log in when we have a broker and an external IDP
    :param settings: settings of the IDP and SP
    :param pytestconfig: fixture that provides the standard used for log in: WSFED or SAML
    :return:
    """
    standard = pytestconfig.getoption('standard')

    s = Session()

    # Standard
    if standard == "WSFED":
        client = "sps_wsfed"
        idp_broker = settings["idp"]["saml_broker"]
    elif standard == "SAML":
        client = "sps_saml"
        idp_broker = settings["idp"]["wsfed_broker"]

    # Service provider settings
    sp = settings[client][0]
    sp_ip = sp["ip"]
    sp_port = sp["port"]
    sp_scheme = sp["http_scheme"]
    sp_path = sp["path"]

    # Identity provider settings
    idp_ip = settings["idp"]["ip"]
    idp_port = settings["idp"]["port"]
    idp_scheme = settings["idp"]["http_scheme"]

    idp2_ip = settings["idp_external"]["ip"]
    idp2_port = settings["idp_external"]["port"]
    idp2_scheme = settings["idp_external"]["http_scheme"]

    idp_username = settings["idp_external"]["test_realm"]["username"]
    idp_password = settings["idp_external"]["test_realm"]["password"]

    keycloak_login_form_id = settings["idp"]["login_form_id"]

    # Common header for all the requests
    header = req.get_header()

    (session_cookie, response) = req.access_sp_saml(logger, s, header, sp_ip,
                                                    sp_port, sp_scheme,
                                                    sp_path, idp_ip, idp_port)

    # store the cookie received from keycloak
    keycloak_cookie = response.cookies

    redirect_url = response.headers['Location']

    header_redirect_idp = {
        **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
        'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
    }

    response = req.redirect_to_idp(logger, s, redirect_url,
                                   header_redirect_idp, keycloak_cookie)

    if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers[
            'WWW-Authenticate'] == 'Negotiate':
        response = req.kerberos_form_fallback(logger, s, response, header, {
            **keycloak_cookie,
            **session_cookie
        })

    # In the login page we can choose to login with the external IDP
    soup = BeautifulSoup(response.content, 'html.parser')

    div = soup.find("div", {"id": "kc-social-providers"})

    # we can have several idp external; choose the one needed for the test
    all_li = div.find_all('li')
    for li in all_li:
        if li.span.text == idp_broker:
            external_idp_url = "{scheme}://{ip}:{port}".format(
                scheme=idp_scheme, ip=idp_ip, port=idp_port) + li.a['href']

    # Select to login with the external IDP
    req_choose_external_idp = Request(method='GET',
                                      url="{url}".format(url=external_idp_url),
                                      headers=header,
                                      cookies=keycloak_cookie)

    prepared_request = req_choose_external_idp.prepare()

    log_request(logger, req_choose_external_idp)

    response = s.send(prepared_request, verify=False, allow_redirects=False)

    logger.debug(response.status_code)

    # get the HTTP binding response with the url to the external IDP
    soup = BeautifulSoup(response.content, 'html.parser')
    form = soup.body.form

    url_form = form.get('action')
    inputs = form.find_all('input')
    method_form = form.get('method')

    params = {}
    for input in inputs:
        params[input.get('name')] = input.get('value')

    header_redirect_external_idp = {
        **header, 'Host': "{ip}:{port}".format(ip=idp2_ip, port=idp2_port),
        'Referer': "{ip}:{port}".format(ip=idp_ip, port=idp_port)
    }

    # Redirect to external IDP
    if idp_broker == "cloudtrust_saml":
        req_redirect_external_idp = Request(
            method=method_form,
            url="{url}".format(url=url_form),
            data=params,
            headers=header_redirect_external_idp)
    else:
        req_redirect_external_idp = Request(
            method=method_form,
            url="{url}".format(url=url_form),
            params=params,
            headers=header_redirect_external_idp)

    referer_url = url_form

    prepared_request = req_redirect_external_idp.prepare()

    log_request(logger, req_redirect_external_idp)

    response = s.send(prepared_request, verify=False, allow_redirects=False)

    logger.debug(response.status_code)

    # if we have an identity provider saml, we do an extra redirect
    if idp_broker == "cloudtrust_saml":
        redirect_url = response.headers['Location']
        keycloak_cookie2 = response.cookies
        response = req.redirect_to_idp(logger, s, redirect_url, header,
                                       keycloak_cookie2)
    else:
        keycloak_cookie2 = response.cookies

    soup = BeautifulSoup(response.content, 'html.parser')

    form = soup.find("form", {"id": keycloak_login_form_id})

    url_form = form.get('action')
    method_form = form.get('method')
    inputs = form.find_all('input')

    input_name = []
    for input in inputs:
        input_name.append(input.get('name'))

    credentials_data = {}
    credentials_data["username"] = idp_username
    credentials_data["password"] = idp_password

    # Authenticate to the external IDP
    response = req.send_credentials_to_idp(logger, s, header, idp2_ip,
                                           idp2_port, referer_url, url_form,
                                           credentials_data, {
                                               **keycloak_cookie2,
                                               **session_cookie
                                           }, method_form)

    keycloak_cookie3 = response.cookies

    # get the HTTP binding response with the url to the broker IDP
    soup = BeautifulSoup(response.content, 'html.parser')
    form = soup.body.form

    url_form = form.get('action')
    inputs = form.find_all('input')
    method_form = form.get('method')

    token = {}
    for input in inputs:
        token[input.get('name')] = input.get('value')

    req_token_from_external_idp = Request(method=method_form,
                                          url="{url}".format(url=url_form),
                                          data=token,
                                          cookies=keycloak_cookie,
                                          headers=header)

    prepared_request = req_token_from_external_idp.prepare()

    log_request(logger, req_token_from_external_idp)

    response = s.send(prepared_request, verify=False, allow_redirects=False)

    logger.debug(response.status_code)

    if response.status_code == HTTPStatus.FOUND:  # user logs in for the first time and has to fill in a form
        response = req.broker_fill_in_form(logger, s, response, header,
                                           keycloak_cookie, idp_broker,
                                           settings)

    # Get the token (SAML response) from the broker IDP
    soup = BeautifulSoup(response.content, 'html.parser')
    form = soup.body.form

    url_form = form.get('action')
    inputs = form.find_all('input')
    method_form = form.get('method')

    token = {}
    for input in inputs:
        token[input.get('name')] = input.get('value')

    # Access SP with the token
    (response,
     sp_cookie) = req.access_sp_with_token(logger, s, header, sp_ip, sp_port,
                                           sp_scheme, idp_scheme, idp_ip,
                                           idp_port, method_form, url_form,
                                           token, session_cookie,
                                           keycloak_cookie)

    return sp_cookie, keycloak_cookie3, response.status_code
    def test_CT_TC_SAML_SSO_FORM_SIMPLE_IDP_initiated_keycloak_endpoint(
            self, settings):
        """
        Test the CT_TC_SAML_SSO_FORM_SIMPLE use case with the IDP-initiated flow, where we set up an endpoint
        on Keycloak with IDP Initiated SSO URL Name.
        Thus, the user accesses
        http[s]://host:port/auth/realms/{RealmName}/protocol/saml/clients/{IDP Initiated SSO URL Name}
        to authenticate to Keycloak and obtain the token (SAML response) and gets redirected
        to the SP that he can access.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_saml"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]
        sp_sso_url_name = sp["sso_url_name"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_test_realm = settings["idp"]["test_realm"]["name"]
        idp_login_endpoint = "auth/realms/{realm}/protocol/saml/clients/{name}".format(
            realm=idp_test_realm, name=sp_sso_url_name)

        idp_username = settings["idp"]["test_realm"]["username"]
        idp_password = settings["idp"]["test_realm"]["password"]
        idp_attr_name = settings["idp"]["test_realm"]["attr_name"]
        idp_attr_tag = settings["idp"]["test_realm"]["attr_xml_elem"]
        idp_attr_name_external = settings["idp"]["test_realm"][
            "external_attr_name"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        # Idp endpoint for client
        url_endpoint = "{scheme}://{ip}:{port}/{path}".format(
            scheme=idp_scheme,
            ip=idp_ip,
            port=idp_port,
            path=idp_login_endpoint)

        req_access_idp_endpoint = Request(
            method='GET',
            url=url_endpoint,
            headers=header,
        )

        prepared_request = req_access_idp_endpoint.prepare()

        log_request(logger, req_access_idp_endpoint)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        keycloak_cookie = response.cookies

        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.find("form", {"id": keycloak_login_form_id})

        assert form is not None

        url_form = form.get('action')
        method_form = form.get('method')

        inputs = form.find_all('input')

        input_name = []
        for input in inputs:
            input_name.append(input.get('name'))

        assert "username" in input_name
        assert "password" in input_name

        # Provide the credentials
        credentials_data = {}
        credentials_data["username"] = idp_username
        credentials_data["password"] = idp_password

        response = req.send_credentials_to_idp(logger, s, header, idp_ip,
                                               idp_port, url_endpoint,
                                               url_form, credentials_data,
                                               keycloak_cookie, method_form)

        assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

        keycloak_cookie_2 = response.cookies

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        decoded_token = base64.b64decode(token['SAMLResponse']).decode("utf-8")

        val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name)
        # assert that the IDP added the location attribute in the token
        assert re.search(val, decoded_token) is not None

        # assert that the external claim is also in the token
        val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name_external)
        assert re.search(val, decoded_token) is not None

        (response, sp_cookie) = req.access_sp_with_token(
            logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme, idp_ip,
            idp_port, method_form, url_form, token, keycloak_cookie_2,
            keycloak_cookie_2)

        assert response.status_code == HTTPStatus.OK

        #  Access the secure page of the SP
        header_sp_page = {
            **header, 'Host': "{ip}:{port}".format(ip=sp_ip, port=sp_port),
            'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
        }

        req_get_sp_page = Request(method='GET',
                                  url="{scheme}://{ip}:{port}/{path}".format(
                                      scheme=sp_scheme,
                                      port=sp_port,
                                      ip=sp_ip,
                                      path=sp_path),
                                  headers=header_sp_page,
                                  cookies=sp_cookie)

        prepared_request = req_get_sp_page.prepare()

        log_request(logger, req_get_sp_page)

        response = s.send(prepared_request, verify=False)

        logger.debug(response.status_code)

        assert response.status_code == HTTPStatus.OK

        assert re.search(sp_message, response.text) is not None
    def test_CT_TC_SAML_SSO_FORM_SIMPLE_SP_initiated(self, settings):
        """
        Test the CT_TC_SAML_SSO_FORM_SIMPLE use case with the SP-initiated flow, i.e. the user accesses the application
        , which is a service provider (SP), that redirects him to the keycloak, the identity provider (IDP).
        The user has to login to keycloak which will give him the SAML token. The token will give him access to the
        application. The token contains builtin and external claims.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_saml"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]

        idp_username = settings["idp"]["test_realm"]["username"]
        idp_password = settings["idp"]["test_realm"]["password"]
        idp_attr_name = settings["idp"]["test_realm"]["attr_name"]
        idp_attr_name_external = settings["idp"]["test_realm"][
            "external_attr_name"]
        idp_attr_tag = settings["idp"]["test_realm"]["attr_xml_elem"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        (session_cookie,
         response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port,
                                        sp_scheme, sp_path, idp_ip, idp_port)

        assert response.status_code == HTTPStatus.FOUND

        # store the cookie received from keycloak
        keycloak_cookie = response.cookies

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp, keycloak_cookie)

        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.find("form", {"id": keycloak_login_form_id})

        assert form is not None

        url_form = form.get('action')
        method_form = form.get('method')

        inputs = form.find_all('input')

        input_name = []
        for input in inputs:
            input_name.append(input.get('name'))

        assert "username" in input_name
        assert "password" in input_name

        # Simulate the login to the identity provider by providing the credentials
        credentials_data = {}
        credentials_data["username"] = idp_username
        credentials_data["password"] = idp_password

        response = req.send_credentials_to_idp(logger, s, header, idp_ip,
                                               idp_port, redirect_url,
                                               url_form, credentials_data,
                                               keycloak_cookie, method_form)

        assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND  #or response.status_code == 303 or response.status_code == 307

        keycloak_cookie_2 = response.cookies

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        decoded_token = base64.b64decode(token['SAMLResponse']).decode("utf-8")

        val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name)
        # assert that the IDP added the location attribute in the token
        assert re.search(val, decoded_token) is not None

        # assert that the external claim is also in the token
        val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name_external)
        assert re.search(val, decoded_token) is not None

        (response, sp_cookie) = req.access_sp_with_token(
            logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme, idp_ip,
            idp_port, method_form, url_form, token, session_cookie,
            keycloak_cookie_2)

        assert response.status_code == HTTPStatus.OK

        # assert that we are logged in
        assert re.search(sp_message, response.text) is not None
Exemple #4
0
    def test_security_fuzzing_wa_wsfed_parameter(self, settings):
        """
        This test respects the following use-case:
        - browser does a GET request to Keycloak (with the goal of performing a login) where one or both of the
        parameters, wa and wtrealm, are fuzzed.
        - after the GET request is done, we check if Keycloak is still working properly by doing a login and a logout
        - repeat the previous steps an unlimited number of times
        - as output we expect to receive 400 and 414 from Keycloak and for the login and logout to be done
        successfully
        :param settings:
        :return:
        """

        s = Session()

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_test_realm = settings["idp"]["test_realm"]["name"]

        # Common header for all the requests
        header = req.get_header()

        # a login WSFED query
        query = "wa=wsignin1.0&" \
                "wreply=http%3A%2F%2F127.0.0.1%3A7000%2Fj_spring_fediz_security_check&" \
                "wtrealm=sp_wsfed1&" \
                "wct=2018-07-10T14%3A43%3A45.921Z&" \
                "wctx=48022b8c-9b80-4446-8487-f94b24439f44"

        initial_redirect_url = "{scheme}://{ip}:{port}/auth/realms/{realm}/protocol/wsfed?{query}".format(
            scheme=idp_scheme,
            ip=idp_ip,
            port=idp_port,
            realm=idp_test_realm,
            query=query)

        while True:
            # split the url in parts
            url_parts = list(url.urlparse(initial_redirect_url))

            # fetch the query part with the wsfed parameters
            query = dict(url.parse_qsl(url_parts[4]))

            # choose what parameters are going to be fuzzed
            random.seed(
                calendar.timegm(time.gmtime()) + random.randint(0, 1000))
            choice = random.randint(0, 2)

            # according to the choice, replace the wsfed parameters with their fuzzed versions
            if choice == 0:
                # fuzz both parameters: wa and wtrealm
                query['wa'] = fuzz.get_fuzzed_value(logger, query['wa'])
                query['wtrealm'] = fuzz.get_fuzzed_value(
                    logger, query['wtrealm'])
            if choice == 1:
                # fuzz the wa parameter
                query['wa'] = fuzz.get_fuzzed_value(logger, query['wa'])
            if choice == 2:
                # fuzz the wtrealm parameter
                query['wtrealm'] = fuzz.get_fuzzed_value(
                    logger, query['wtrealm'])

            # recreate the url
            url_parts[4] = url.urlencode(query)
            fuzzed_redirect_url = url.urlunparse(url_parts)

            if fuzzed_redirect_url != initial_redirect_url:
                logger.info(
                    "Sending a wsfed login request with the fuzzed url {url}".
                    format(url=fuzzed_redirect_url))
                req_get_keycloak = Request(
                    method='GET',
                    url="{url}".format(url=fuzzed_redirect_url),
                    headers=header)

                prepared_request = req_get_keycloak.prepare()
                req.log_request(logger, req_get_keycloak)
                response = s.send(prepared_request, verify=False)
                logger.info(response.status_code)

                #assert response.status_code == HTTPStatus.BAD_REQUEST

                # check that Keycloak is up there running and able to answer to requests
                # run the wsfed login test
                s = Session()

                # Service provider settings
                sp = settings["sps_wsfed"][0]
                sp_ip = sp["ip"]
                sp_port = sp["port"]
                sp_scheme = sp["http_scheme"]
                sp_path = sp["path"]
                sp_message = sp["logged_in_message"]
                sp_logout_path = sp["logout_path"]
                sp_logout_message = sp["logged_out_message"]

                # Identity provider settings
                idp_ip = settings["idp"]["ip"]
                idp_port = settings["idp"]["port"]
                idp_scheme = settings["idp"]["http_scheme"]

                idp_username = settings["idp"]["test_realm"]["username"]
                idp_password = settings["idp"]["test_realm"]["password"]

                keycloak_login_form_id = settings["idp"]["login_form_id"]

                # Common header for all the requests
                header = req.get_header()

                response = req.access_sp_ws_fed(logger, s, header, sp_ip,
                                                sp_port, sp_scheme, sp_path)

                session_cookie = response.cookies

                redirect_url = response.headers['Location']

                header_redirect_idp = {
                    **header, 'Host':
                    "{ip}:{port}".format(ip=idp_ip, port=idp_port),
                    'Referer':
                    "{ip}:{port}".format(ip=sp_ip, port=sp_port)
                }

                response = req.redirect_to_idp(logger, s, redirect_url,
                                               header_redirect_idp,
                                               session_cookie)

                keycloak_cookie = response.cookies

                if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers[
                        'WWW-Authenticate'] == 'Negotiate':
                    response = req.kerberos_form_fallback(
                        logger, s, response, header, {
                            **keycloak_cookie,
                            **session_cookie
                        })

                soup = BeautifulSoup(response.content, 'html.parser')

                form = soup.find("form", {"id": keycloak_login_form_id})

                assert form is not None

                url_form = form.get('action')
                method_form = form.get('method')

                inputs = form.find_all('input')

                input_name = []
                for input in inputs:
                    input_name.append(input.get('name'))

                assert "username" in input_name
                assert "password" in input_name

                # Simulate the login to the identity provider by providing the credentials
                credentials_data = {}
                credentials_data["username"] = idp_username
                credentials_data["password"] = idp_password

                response = req.send_credentials_to_idp(
                    logger, s, header, idp_ip, idp_port, redirect_url,
                    url_form, credentials_data, keycloak_cookie, method_form)

                assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND  # or response.status_code == 303 or response.status_code == 307

                keycloak_cookie_2 = response.cookies

                soup = BeautifulSoup(response.content, 'html.parser')
                form = soup.body.form

                url_form = form.get('action')
                inputs = form.find_all('input')
                method_form = form.get('method')

                # Get the token from the identity provider
                token = {}
                for input in inputs:
                    token[input.get('name')] = input.get('value')

                (response, sp_cookie) = req.access_sp_with_token(
                    logger,
                    s,
                    header,
                    sp_ip,
                    sp_port,
                    sp_scheme,
                    idp_scheme,
                    idp_ip,
                    idp_port,
                    method_form,
                    url_form,
                    token,
                    session_cookie,
                    keycloak_cookie_2,
                )

                assert response.status_code == HTTPStatus.OK
                logger.info("Login returned a {code} status code".format(
                    code=response.status_code))

                # assert that we are logged in
                assert re.search(sp_message, response.text) is not None

                # run the wsfed logout test

                # Access to the SP logout page
                header_sp_logout_page = {
                    **header, 'Host':
                    "{ip}:{port}".format(ip=sp_ip, port=sp_port),
                    'Referer':
                    "{scheme}://{ip}:{port}".format(scheme=sp_scheme,
                                                    ip=sp_ip,
                                                    port=sp_port)
                }

                req_get_sp_logout_page = Request(
                    method='GET',
                    url="{scheme}://{ip}:{port}/{path}".format(
                        scheme=sp_scheme,
                        port=sp_port,
                        ip=sp_ip,
                        path=sp_logout_path),
                    headers=header_sp_logout_page,
                    cookies=sp_cookie)

                prepared_request = req_get_sp_logout_page.prepare()

                req.log_request(logger, req_get_sp_logout_page)

                response = s.send(prepared_request,
                                  verify=False,
                                  allow_redirects=False)

                logger.debug(response.status_code)

                redirect_url = response.headers['Location']

                req_sp_logout_redirect = Request(method='GET',
                                                 url=redirect_url,
                                                 headers=header_sp_logout_page,
                                                 cookies={**sp_cookie})

                prepared_request = req_sp_logout_redirect.prepare()

                req.log_request(logger, req_sp_logout_redirect)

                response = s.send(prepared_request,
                                  verify=False,
                                  allow_redirects=False)

                logger.debug(response.status_code)

                redirect_url = response.headers['Location']

                response = req.redirect_to_idp(logger, s, redirect_url, header,
                                               {
                                                   **sp_cookie,
                                                   **keycloak_cookie_2
                                               })

                assert response.status_code == HTTPStatus.OK

                soup = BeautifulSoup(response.content, 'html.parser')

                form = soup.body.form
                url_form = form.get('action')
                method_form = form.get('method')
                inputs = form.find_all('input')

                # Send the token
                token = {}
                for input in inputs:
                    token[input.get('name')] = input.get('value')

                (response, cookie) = req.access_sp_with_token(
                    logger,
                    s,
                    header,
                    sp_ip,
                    sp_port,
                    sp_scheme,
                    idp_scheme,
                    idp_ip,
                    idp_port,
                    method_form,
                    url_form,
                    token,
                    sp_cookie,
                    sp_cookie,
                )

                assert response.status_code == HTTPStatus.OK

                # assert we are logged out
                assert re.search(sp_logout_message, response.text) is not None
                logger.info("Logout returned a {code} status code".format(
                    code=response.status_code))
    def test_CT_TC_WS_FED_IDP_LOGOUT_SIMPLE(self, settings, login_sso_form):
        """
        Test the CT_TC_WS_FED_IDP_LOGOUT_SIMPLE use case with the SP-initiated flow, i.e. the user that accessed the SP
        asks to be logged out. This will trigger the logout to be performed on the IDP side and the user will
        be able to see the "You're logged out" page.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sps = [settings["sps_wsfed"][0]]
        for sp in sps:
            sp_ip = sp["ip"]
            sp_port = sp["port"]
            sp_scheme = sp["http_scheme"]
            sp_logout_path = sp["logout_path"]
            sp_message = sp["logged_out_message"]

            # Identity provider settings
            idp_ip = settings["idp"]["ip"]
            idp_port = settings["idp"]["port"]
            idp_scheme = settings["idp"]["http_scheme"]

            # Common header for all the requests
            header = req.get_header()

            # Perform login using the fixture login_sso_form
            sp_cookie, keycloak_cookie = login_sso_form

            # User is logged in

            # Access to the SP logout page
            header_sp_logout_page = {
                **header,
                'Host': "{ip}:{port}".format(ip=sp_ip, port=sp_port),
                'Referer': "{scheme}://{ip}:{port}".format(scheme=sp_scheme, ip=sp_ip, port=sp_port)
            }

            req_get_sp_logout_page = Request(
                method='GET',
                url="{scheme}://{ip}:{port}/{path}".format(
                    scheme=sp_scheme,
                    port=sp_port,
                    ip=sp_ip,
                    path=sp_logout_path
                ),
                headers=header_sp_logout_page,
                cookies=sp_cookie
            )

            prepared_request = req_get_sp_logout_page.prepare()

            log_request(logger, req_get_sp_logout_page)

            response = s.send(prepared_request, verify=False, allow_redirects=False)

            logger.debug(response.status_code)

            redirect_url = response.headers['Location']

            req_sp_logout_redirect = Request(
                method='GET',
                url= redirect_url,
                headers=header_sp_logout_page,
                cookies={**sp_cookie}
            )

            prepared_request = req_sp_logout_redirect.prepare()

            log_request(logger, req_sp_logout_redirect)

            response = s.send(prepared_request, verify=False, allow_redirects=False)

            logger.debug(response.status_code)

            redirect_url = response.headers['Location']

            response = req.redirect_to_idp(logger, s, redirect_url, header, {**sp_cookie, **keycloak_cookie})

            assert response.status_code == HTTPStatus.OK

            soup = BeautifulSoup(response.content, 'html.parser')

            form = soup.body.form
            url_form = form.get('action')
            method_form = form.get('method')
            inputs = form.find_all('input')

            # Send the token
            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            (response, cookie) = req.access_sp_with_token(logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme, idp_ip, idp_port,
                                                          method_form, url_form, token, sp_cookie, sp_cookie, )

            assert response.status_code == HTTPStatus.OK

            assert re.search(sp_message, response.text) is not None
Exemple #6
0
    def test_CT_TC_SAML_SSO_BROKER_SIMPLE_IDP_initiated(self, settings):
        """
        Test the CT_TC_SAML_SSO_BROKER_SIMPLE use case with the IDP-initiated flow, i.e. the user logs in keycloak,
        the identity provider (IDP), and then accesses the application, which is a service provider (SP).
        The application redirects towards keycloak to obtain the SAML token.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_saml"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_test_realm = settings["idp"]["test_realm"]["name"]
        idp_path = "auth/realms/{realm}/account".format(realm=idp_test_realm)
        idp_message = settings["idp"]["logged_in_message"]
        idp_form_id = settings["idp"]["login_form_update"]

        idp_username = settings["idp_external"]["test_realm"]["username"]
        idp_password = settings["idp_external"]["test_realm"]["password"]


        idp2_ip = settings["idp_external"]["ip"]
        idp2_port = settings["idp_external"]["port"]
        idp2_scheme = settings["idp_external"]["http_scheme"]

        idp2_external_test_realm = settings["idp_external"]["test_realm"]["name"]
        idp2_path = "auth/realms/{realm}/account".format(realm=idp2_external_test_realm)
        idp2_message = settings["idp"]["logged_in_message"]

        idp_attr_name = settings["idp"]["test_realm"]["attr_name"]
        idp_attr_name_external = settings["idp"]["test_realm"]["external_attr_name"]
        idp_attr_tag = settings["idp"]["test_realm"]["attr_xml_elem"]

        idp_attr_name_broker = settings["idp_external"]["test_realm"]["attr_name"]
        idp_attr_tag_broker = settings["idp_external"]["test_realm"]["attr_xml_elem"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        # We check that test works for both types of identity provider
        idp_brokers = [settings["idp"]["saml_broker"], settings["idp"]["wsfed_broker"]]

        for idp_broker in idp_brokers:

            # Login to the external IDP
            (oath_cookie, keycloak_cookie3, response) = req.login_external_idp(logger, s,
                                                                               header, idp_ip, idp_port,
                                                                               idp_scheme, idp_path,
                                                                               idp_username, idp_password,
                                                                               idp2_ip, idp2_port, idp_broker,
                                                                               idp_form_id)

            assert response.status_code == HTTPStatus.OK

            # Assert we are logged in
            assert re.search(idp_message, response.text) is not None

            (session_cookie, response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port, sp_scheme, sp_path, idp_ip, idp_port)

            # store the cookie received from keycloak
            keycloak_cookie5 = response.cookies

            assert response.status_code == HTTPStatus.FOUND

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header,
                'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url, header_redirect_idp, {**keycloak_cookie5, **keycloak_cookie3})

            assert response.status_code == HTTPStatus.OK

            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            # Get the token (SAML response) from the broker identity provider
            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            decoded_token = base64.b64decode(token['SAMLResponse']).decode("utf-8")

            val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name)
            # assert that the IDP added the location attribute in the token
            assert re.search(val, decoded_token) is not None

            # assert that the external claim is also in the token
            val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name_external)
            assert re.search(val, decoded_token) is not None

            # assert that the claims that come from the external IDP are well in the token
            val = idp_attr_tag_broker + "=\"{v}\"".format(v=idp_attr_name_broker)
            # assert that the IDP added the location attribute in the token
            assert re.search(val, decoded_token) is not None

            (response, sp_cookie) = req.access_sp_with_token(logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme,
                                                             idp_ip, idp_port, method_form, url_form, token, session_cookie,
                                                             keycloak_cookie5)

            assert response.status_code == HTTPStatus.OK

            assert re.search(sp_message, response.text) is not None
    def test_CT_TC_SAML_IDP_ACCESS_CONTROL_RBAC_OK_IDP_initiated(
            self, settings):
        """
        Scenario: User logs in to the IDP. He then accesses SP1 where he has the appropriate role.
        Same user tries to log in to SP2, SP that he is authorized to access. He should
        be able to access SP2 without authenticating again.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_saml"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Service provider 2 settings
        sp2 = settings["sps_saml"][1]
        sp2_ip = sp2["ip"]
        sp2_port = sp2["port"]
        sp2_scheme = sp2["http_scheme"]
        sp2_path = sp2["path"]
        sp2_message = sp2["logged_in_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_message = settings["idp"]["logged_in_message"]
        idp_test_realm = settings["idp"]["test_realm"]["name"]
        idp_path = "auth/realms/{realm}/account".format(realm=idp_test_realm)

        idp_username = settings["idp"]["test_realm"]["username"]
        idp_password = settings["idp"]["test_realm"]["password"]

        # Common header for all the requests
        header = req.get_header()

        # Perform login to IDP
        (oath_cookie, keycloak_cookie, keycloak_cookie2,
         response) = req.login_idp(logger, s, header, idp_ip, idp_port,
                                   idp_scheme, idp_path, idp_username,
                                   idp_password)

        assert response.status_code == HTTPStatus.OK

        # Assert we are logged in to IDP
        assert re.search(idp_message, response.text) is not None

        # Access SP1
        (session_cookie,
         response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port,
                                        sp_scheme, sp_path, idp_ip, idp_port)

        # store the cookie received from keycloak
        keycloak_cookie3 = response.cookies

        assert response.status_code == HTTPStatus.FOUND

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp, {
                                           **keycloak_cookie3,
                                           **keycloak_cookie2
                                       })

        assert response.status_code == HTTPStatus.OK

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        (response, sp_cookie) = req.access_sp_with_token(
            logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme, idp_ip,
            idp_port, method_form, url_form, token, session_cookie,
            keycloak_cookie2)

        assert response.status_code == HTTPStatus.OK

        assert re.search(sp_message, response.text) is not None

        # User can access SP1

        # Attempt to access SP2

        (session_cookie,
         response) = req.access_sp_saml(logger, s, header, sp2_ip, sp2_port,
                                        sp2_scheme, sp2_path, idp_ip, idp_port)

        session_cookie2 = response.cookies

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp2_ip, port=sp2_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp, {
                                           **session_cookie2,
                                           **keycloak_cookie2
                                       })

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        (response, sp2_cookie) = req.access_sp_with_token(
            logger, s, header, sp2_ip, sp2_port, sp2_scheme, idp_scheme,
            idp_ip, idp_port, method_form, url_form, token, session_cookie,
            session_cookie2)

        assert response.status_code == HTTPStatus.OK

        assert re.search(sp2_message, response.text) is not None
Exemple #8
0
    def test_CT_TC_SAML_SSO_FORM_SIMPLE_IDP_initiated(self, settings):
        """
        Test the CT_TC_SAML_SSO_FORM_SIMPLE use case with the IDP-initiated flow, i.e. the user logs in keycloak,
        the identity provider (IDP), and then accesses the application, which is a service provider (SP).
        The application redirect towards keycloak to obtain the SAML token.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sps = [settings["sps_saml"][0], settings["sps_saml"][1]]
        for sp in sps:
            sp_ip = sp["ip"]
            sp_port = sp["port"]
            sp_scheme = sp["http_scheme"]
            sp_path = sp["path"]
            sp_message = sp["logged_in_message"]

            # Identity provider settings
            idp_ip = settings["idp"]["ip"]
            idp_port = settings["idp"]["port"]
            idp_scheme = settings["idp"]["http_scheme"]
            idp_test_realm = settings["idp"]["test_realm"]["name"]
            idp_path = "auth/realms/{realm}/account".format(realm=idp_test_realm)
            idp_message = settings["idp"]["logged_in_message"]

            idp_username = settings["idp"]["test_realm"]["username"]
            idp_password = settings["idp"]["test_realm"]["password"]


            # Common header for all the requests
            header = req.get_header()

            (oath_cookie, keycloak_cookie, keycloak_cookie2, response) = req.login_idp(logger, s, header, idp_ip, idp_port, idp_scheme,
                                                                                    idp_path, idp_username, idp_password)

            assert response.status_code == HTTPStatus.OK

            # Assert we are logged in
            assert re.search(idp_message, response.text) is not None

            (session_cookie, response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port, sp_scheme, sp_path, idp_ip, idp_port)

            # store the cookie received from keycloak
            keycloak_cookie3 = response.cookies

            assert response.status_code == HTTPStatus.FOUND

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header,
                'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url, header_redirect_idp, {**keycloak_cookie3, **keycloak_cookie2})

            assert response.status_code == HTTPStatus.OK

            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            # Get the token (SAML response) from the identity provider
            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            (response, sp_cookie) = req.access_sp_with_token(logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme,
                                                             idp_ip, idp_port, method_form, url_form, token, session_cookie,
                                                             keycloak_cookie2)

            assert response.status_code == HTTPStatus.OK

            assert re.search(sp_message, response.text) is not None
    def test_CT_TC_WS_FED_IDP_LOGOUT_PERIMETRIC(self, settings,
                                                login_sso_form):
        """
        Scenario: user is logged in on several SPs.
        The user logs out of one SP. Access to all SPs should require a new log in.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp1 = settings["sps_wsfed"][0]
        sp_ip = sp1["ip"]
        sp_port = sp1["port"]
        sp_scheme = sp1["http_scheme"]
        sp_path = sp1["path"]
        sp_logout_path = sp1["logout_path"]
        sp_message = sp1["logged_out_message"]

        sp2 = settings["sps_wsfed"][1]
        sp2_ip = sp2["ip"]
        sp2_port = sp2["port"]
        sp2_scheme = sp2["http_scheme"]
        sp2_path = sp2["path"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]

        # Common header for all the requests
        header = req.get_header()

        # Perform login using the fixture login_sso_form
        sp_cookie, keycloak_cookie = login_sso_form

        # User is logged in on SP1

        # Perform login on SP2

        response = req.access_sp_ws_fed(logger, s, header, sp2_ip, sp2_port,
                                        sp2_scheme, sp2_path)

        session_cookie = response.cookies

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp2_ip, port=sp2_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp,
                                       {**keycloak_cookie})

        if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers[
                'WWW-Authenticate'] == 'Negotiate':
            response = req.kerberos_form_fallback(logger, s, response, header,
                                                  {
                                                      **keycloak_cookie,
                                                      **session_cookie
                                                  })

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        (response, sp2_cookie) = req.access_sp_with_token(
            logger,
            s,
            header,
            sp2_ip,
            sp2_port,
            sp2_scheme,
            idp_scheme,
            idp_ip,
            idp_port,
            method_form,
            url_form,
            token,
            session_cookie,
            keycloak_cookie,
        )

        # req_get_sp_login_reload_page = Request(
        #     method='GET',
        #     url="{scheme}://{ip}:{port}/{path}".format(
        #         scheme=sp2_scheme,
        #         port=sp2_port,
        #         ip=sp2_ip,
        #         path=sp2_path
        #     ),
        #     headers=header_sp2_reload_page,
        #     cookies={**session_cookie}
        # )
        #
        # prepared_request = req_get_sp_login_reload_page.prepare()
        #
        # logger.debug(
        #     json.dumps(
        #         prepared_request_to_json(req_get_sp_login_reload_page),
        #         sort_keys=True,
        #         indent=4,
        #         separators=(',', ': ')
        #     )
        # )
        #
        # response = s.send(prepared_request, verify=False, allow_redirects=False)
        #
        # logger.debug(response.status_code)
        #
        # # the user is logged in and refreshing the page will return an OK
        # assert response.status_code == 200

        # User is now logged in on both applications: SP1 and SP2

        # Logout from the first applications
        header_sp_logout_page = {
            **header, 'Host':
            "{ip}:{port}".format(ip=sp_ip, port=sp_port),
            'Referer':
            "{scheme}://{ip}:{port}".format(scheme=sp_scheme,
                                            ip=sp_ip,
                                            port=sp_port)
        }

        req_get_sp_logout_page = Request(
            method='GET',
            url="{scheme}://{ip}:{port}/{path}".format(scheme=sp_scheme,
                                                       port=sp_port,
                                                       ip=sp_ip,
                                                       path=sp_logout_path),
            headers=header_sp_logout_page,
            cookies={**sp_cookie})

        prepared_request = req_get_sp_logout_page.prepare()

        log_request(logger, req_get_sp_logout_page)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        # new session cookie
        session_cookie2 = response.cookies

        redirect_url = response.headers['Location']

        req_sp_logout_redirect = Request(method='GET',
                                         url=redirect_url,
                                         headers=header_sp_logout_page,
                                         cookies={**sp_cookie})

        prepared_request = req_sp_logout_redirect.prepare()

        log_request(logger, req_sp_logout_redirect)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        redirect_url = response.headers['Location']

        response = req.redirect_to_idp(logger, s, redirect_url, header,
                                       sp_cookie)

        assert response.status_code == HTTPStatus.OK

        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.body.form
        url_form = form.get('action')
        method_form = form.get('method')
        inputs = form.find_all('input')

        # Send ws fed response
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        (response,
         cookie) = req.access_sp_with_token(logger, s, header, sp_ip, sp_port,
                                            sp_scheme, idp_scheme, idp_ip,
                                            idp_port, method_form, url_form,
                                            token, sp_cookie, sp_cookie)

        assert response.status_code == HTTPStatus.OK

        assert re.search(sp_message, response.text) is not None

        # Check that when the user accesses the secured page of SP1 with the old session cookie,
        # he is redirected to log in

        req_get_sp_login_reload_page = Request(
            method='GET',
            url="{scheme}://{ip}:{port}/{path}".format(scheme=sp_scheme,
                                                       port=sp_port,
                                                       ip=sp_ip,
                                                       path=sp_path),
            headers=header,
            cookies={**session_cookie})

        prepared_request = req_get_sp_login_reload_page.prepare()

        log_request(logger, req_get_sp_login_reload_page)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        # Assert that the refresh page gives a 302 which signals that the user is logged out of SP
        assert response.status_code == HTTPStatus.FOUND

        # Check if the user is logged out from SP2: perform a refresh of the page; we expect to get a redirect

        header_sp2_reload_page = {
            **header,
            'Host': "{ip}:{port}".format(ip=sp2_ip, port=sp2_port),
        }

        req_get_sp_login_reload_page = Request(
            method='GET',
            url="{scheme}://{ip}:{port}/{path}".format(scheme=sp2_scheme,
                                                       port=sp2_port,
                                                       ip=sp2_ip,
                                                       path=sp2_path),
            headers=header_sp2_reload_page,
            cookies={**session_cookie})

        prepared_request = req_get_sp_login_reload_page.prepare()

        log_request(logger, req_get_sp_login_reload_page)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        # Assert that the refresh page gives a 302 which signals that the user is logged out of SP2
        assert response.status_code == HTTPStatus.FOUND
Exemple #10
0
    def test_CT_TC_SAML_SSO_FORM_SIMPLE__ARTIFACT_BINDING_SP_initiated(self, settings):
        """
        Test the CT_TC_SAML_SSO_FORM_SIMPLE use case with the SP-initiated flow, i.e. the user accesses the application
        , which is a service provider (SP), that redirects him to the keycloak, the identity provider (IDP).
        The user has to login to keycloak.
        The IDP sends an artifact encoded in SAMLart and then IDP and SP discuss directly (not passing through the browser)
        in order to obtain the SAML token. The token will give to the user the access to the
        application.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sps = [settings["sps_saml"][0]]
        for sp in sps:
            sp_ip = sp["ip"]
            sp_port = sp["port"]
            sp_scheme = sp["http_scheme"]
            sp_path = sp["path"]
            sp_message = sp["logged_in_message"]

            # Identity provider settings
            idp_ip = settings["idp"]["ip"]
            idp_port = settings["idp"]["port"]
            idp_scheme = settings["idp"]["http_scheme"]

            idp_username = settings["idp"]["test_realm"]["username"]
            idp_password = settings["idp"]["test_realm"]["password"]

            keycloak_login_form_id = settings["idp"]["login_form_id"]

            # Common header for all the requests
            header = req.get_header()

            (session_cookie, response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port, sp_scheme, sp_path,
                                                                            idp_ip, idp_port)

            assert response.status_code == HTTPStatus.FOUND

            # store the cookie received from keycloak
            keycloak_cookie = response.cookies

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header,
                'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url, header_redirect_idp, keycloak_cookie)

            if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers['WWW-Authenticate'] == 'Negotiate':
                response = req.kerberos_form_fallback(logger, s, response, header,
                                                      {**keycloak_cookie, **session_cookie})

            soup = BeautifulSoup(response.content, 'html.parser')

            form = soup.find("form", {"id": keycloak_login_form_id})

            assert form is not None

            url_form = form.get('action')
            method_form = form.get('method')

            inputs = form.find_all('input')

            input_name = []
            for input in inputs:
                    input_name.append(input.get('name'))

            assert "username" in input_name
            assert "password" in input_name

            # Simulate the login to the identity provider by providing the credentials
            credentials_data = {}
            credentials_data["username"] = idp_username
            credentials_data["password"] = idp_password

            response = req.send_credentials_to_idp(logger, s, header, idp_ip, idp_port, redirect_url, url_form, credentials_data, keycloak_cookie, method_form)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND #or response.status_code == 303 or response.status_code == 307



            # While normally at this stage we would get the token (SAML response) from the identity provider,
            # for this use case, we get the artifact encoded in the parameter SAMLart

            assert response.status_code == HTTPStatus.FOUND  # HTTPStatus.OK
            redirect_url = response.headers['Location']

            # extract the SAMLart
            parsed = urlparse.urlparse(redirect_url)
            artifact = urlparse.parse_qs(parsed.query)['SAMLart']

            # assert we received the artifact in encoded in the SAMLart parameter
            assert artifact is not None

            req_get_artifact = Request(
                method='GET',
                url="{url}".format(url=redirect_url),
                cookies={**session_cookie, **keycloak_cookie},
                headers=header
            )

            prepared_request = req_get_artifact.prepare()

            log_request(logger, req_get_artifact)

            response = s.send(prepared_request, verify=False)

            logger.debug(response.status_code)


            # wait for IDP to send the SAML token to SP
            time.sleep(2)

            # assert that we are logged in
            assert re.search(sp_message, response.text) is not None
    def test_CT_TC_SAML_SSO_FORM_SIMPLE_SP_initiated(self, settings):
        """
        Test the CT_TC_SAML_SSO_FORM_SIMPLE use case with the SP-initiated flow, i.e. the user accesses the application
        , which is a service provider (SP), that redirects him to the keycloak, the identity provider (IDP).
        The user has to login to keycloak which will give him the SAML token. The token will give him access to the
        application.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sps = [settings["sps_saml"][0], settings["sps_saml"][1]]
        for sp in sps:
            sp_ip = sp["ip"]
            sp_port = sp["port"]
            sp_scheme = sp["http_scheme"]
            sp_path = sp["path"]
            sp_message = sp["logged_in_message"]

            # Identity provider settings
            idp_ip = settings["idp"]["ip"]
            idp_port = settings["idp"]["port"]
            idp_scheme = settings["idp"]["http_scheme"]

            idp_username = settings["idp"]["test_realm"]["username"]
            idp_password = settings["idp"]["test_realm"]["password"]

            keycloak_login_form_id = settings["idp"]["login_form_id"]

            # Common header for all the requests
            header = req.get_header()

            (session_cookie, response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port, sp_scheme, sp_path,
                                                                            idp_ip, idp_port)

            assert response.status_code == HTTPStatus.FOUND

            # store the cookie received from keycloak
            keycloak_cookie = response.cookies

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header,
                'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url, header_redirect_idp, keycloak_cookie)

            if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers['WWW-Authenticate'] == 'Negotiate':
                response = req.kerberos_form_fallback(logger, s, response, header,
                                                      {**keycloak_cookie, **session_cookie})

            soup = BeautifulSoup(response.content, 'html.parser')

            form = soup.find("form", {"id": keycloak_login_form_id})

            assert form is not None

            url_form = form.get('action')
            method_form = form.get('method')

            inputs = form.find_all('input')

            input_name = []
            for input in inputs:
                    input_name.append(input.get('name'))

            assert "username" in input_name
            assert "password" in input_name

            # Simulate the login to the identity provider by providing the credentials
            credentials_data = {}
            credentials_data["username"] = idp_username
            credentials_data["password"] = idp_password

            response = req.send_credentials_to_idp(logger, s, header, idp_ip, idp_port, redirect_url, url_form, credentials_data, keycloak_cookie, method_form)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND #or response.status_code == 303 or response.status_code == 307

            keycloak_cookie_2 = response.cookies

            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            # Get the token (SAML response) from the identity provider
            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            (response, sp_cookie) = req.access_sp_with_token(logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme,
                                                             idp_ip, idp_port, method_form, url_form, token, session_cookie,
                                                             keycloak_cookie_2)

            assert response.status_code == HTTPStatus.OK

            # assert that we are logged in
            assert re.search(sp_message, response.text) is not None
Exemple #12
0
    def test_CT_TC_WS_FED_BROKER_SIMPLE_SP_initiated(self, settings):
        """
        Test the CT_TC_WS_FED_BROKER_SIMPLE use case with the SP-initiated flow, i.e. the user accesses the application
        , which is a service provider (SP), that redirects him to the keycloak, the identity provider (IDP).
        The user has to login to keycloak which will give him the SAML token. The token will give him access to the
        application.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_wsfed"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_form_id = settings["idp"]["login_form_update"]

        idp_username = settings["idp_external"]["test_realm"]["username"]
        idp_password = settings["idp_external"]["test_realm"]["password"]

        idp2_ip = settings["idp_external"]["ip"]
        idp2_port = settings["idp_external"]["port"]
        idp2_scheme = settings["idp_external"]["http_scheme"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        # We check that login works for both types of identity provider
        idp_brokers = [
            settings["idp"]["saml_broker"], settings["idp"]["wsfed_broker"]
        ]

        for idp_broker in idp_brokers:

            response = req.access_sp_ws_fed(logger, s, header, sp_ip, sp_port,
                                            sp_scheme, sp_path)

            session_cookie = response.cookies

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp_ip,
                                                       port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url,
                                           header_redirect_idp, session_cookie)

            keycloak_cookie = response.cookies

            if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers[
                    'WWW-Authenticate'] == 'Negotiate':
                response = req.kerberos_form_fallback(logger, s, response,
                                                      header, {
                                                          **keycloak_cookie,
                                                          **session_cookie
                                                      })

            # In the login page we can choose to login with the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')

            div = soup.find("div", {"id": "kc-social-providers"})

            assert div is not None

            # we can have several idp external; choose the one needed for the test
            all_li = div.find_all('li')
            for li in all_li:
                if li.span.text == idp_broker:
                    external_idp_url = "{scheme}://{ip}:{port}".format(
                        scheme=idp_scheme, ip=idp_ip,
                        port=idp_port) + li.a['href']

            assert external_idp_url is not None

            # Select to login with the external IDP
            req_choose_external_idp = Request(
                method='GET',
                url="{url}".format(url=external_idp_url),
                headers=header,
                cookies=keycloak_cookie)

            prepared_request = req_choose_external_idp.prepare()

            log_request(logger, req_choose_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            logger.debug(response.status_code)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            params = {}
            for input in inputs:
                params[input.get('name')] = input.get('value')

            header_redirect_external_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp2_ip,
                                                       port=idp2_port),
                'Referer': "{ip}:{port}".format(ip=idp_ip, port=idp_port)
            }

            # Redirect to external IDP
            if idp_broker == "cloudtrust_saml":
                req_redirect_external_idp = Request(
                    method=method_form,
                    url="{url}".format(url=url_form),
                    data=params,
                    headers=header_redirect_external_idp)
            else:
                req_redirect_external_idp = Request(
                    method=method_form,
                    url="{url}".format(url=url_form),
                    params=params,
                    headers=header_redirect_external_idp)

            referer_url = url_form

            prepared_request = req_redirect_external_idp.prepare()

            log_request(logger, req_redirect_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            logger.debug(response.status_code)

            # if we have an identity provider saml, we do an extra redirect
            if idp_broker == "cloudtrust_saml":
                redirect_url = response.headers['Location']
                keycloak_cookie2 = response.cookies
                response = req.redirect_to_idp(logger, s, redirect_url, header,
                                               keycloak_cookie2)
            else:
                keycloak_cookie2 = response.cookies

            soup = BeautifulSoup(response.content, 'html.parser')

            form = soup.find("form", {"id": keycloak_login_form_id})

            assert form is not None

            url_form = form.get('action')
            method_form = form.get('method')
            inputs = form.find_all('input')

            input_name = []
            for input in inputs:
                input_name.append(input.get('name'))

            assert "username" in input_name
            assert "password" in input_name

            credentials_data = {}
            credentials_data["username"] = idp_username
            credentials_data["password"] = idp_password

            # Authenticate to the external IDP
            response = req.send_credentials_to_idp(logger, s, header, idp2_ip,
                                                   idp2_port, referer_url,
                                                   url_form, credentials_data,
                                                   {
                                                       **keycloak_cookie2,
                                                       **session_cookie
                                                   }, method_form)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            req_token_from_external_idp = Request(
                method=method_form,
                url="{url}".format(url=url_form),
                data=token,
                cookies=keycloak_cookie,
                headers=header)

            prepared_request = req_token_from_external_idp.prepare()

            log_request(logger, req_token_from_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            logger.debug(response.status_code)

            if response.status_code == HTTPStatus.FOUND:
                new_cookie = response.cookies
                redirect_url = response.headers['Location']
                response = req.redirect_to_idp(logger, s, redirect_url, header,
                                               {
                                                   **keycloak_cookie,
                                                   **new_cookie
                                               })
                response = req.broker_fill_in_form(logger, s, response, header,
                                                   keycloak_cookie, new_cookie,
                                                   idp_broker, idp_form_id)

            # Get the token from the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            # Access SP with the token
            (response, sp_cookie) = req.access_sp_with_token(
                logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme,
                idp_ip, idp_port, method_form, url_form, token, session_cookie,
                keycloak_cookie2)

            assert response.status_code == HTTPStatus.OK

            # assert that we are logged in
            assert re.search(sp_message, response.text) is not None
Exemple #13
0
    def test_CT_TC_WS_FED_IDP_ACCESS_CONTROL_ABAC_KO_SP_initiated(
            self, settings):
        """
        Scenario: User logs in to SP1 where he has the appropriate attribute.
        Same user tries to access SP2, SP that he is not authorized to access. He should receive an
        error message saying he has not the authorization.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_wsfed"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Service provider 2 settings
        sp2 = settings["sps_wsfed"][2]
        sp2_ip = sp2["ip"]
        sp2_port = sp2["port"]
        sp2_scheme = sp2["http_scheme"]
        sp2_path = sp2["path"]
        sp2_message = settings["idp"]["not_authorized_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]

        idp_username = settings["idp"]["test_realm"]["username"]
        idp_password = settings["idp"]["test_realm"]["password"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        # Perform login to SP1

        response = req.access_sp_ws_fed(logger, s, header, sp_ip, sp_port,
                                        sp_scheme, sp_path)

        session_cookie = response.cookies

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp, session_cookie)

        keycloak_cookie = response.cookies

        if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers[
                'WWW-Authenticate'] == 'Negotiate':
            response = req.kerberos_form_fallback(logger, s, response, header,
                                                  {
                                                      **keycloak_cookie,
                                                      **session_cookie
                                                  })

        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.find("form", {"id": keycloak_login_form_id})

        assert form is not None

        url_form = form.get('action')
        method_form = form.get('method')

        inputs = form.find_all('input')

        input_name = []
        for input in inputs:
            input_name.append(input.get('name'))

        assert "username" in input_name
        assert "password" in input_name

        # Simulate the login to the identity provider by providing the credentials
        credentials_data = {}
        credentials_data["username"] = idp_username
        credentials_data["password"] = idp_password

        response = req.send_credentials_to_idp(logger, s, header, idp_ip,
                                               idp_port, redirect_url,
                                               url_form, credentials_data,
                                               keycloak_cookie, method_form)

        assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND  #or response.status_code == 303 or response.status_code == 307

        keycloak_cookie_2 = response.cookies

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        (response, sp_cookie) = req.access_sp_with_token(
            logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme, idp_ip,
            idp_port, method_form, url_form, token, session_cookie,
            keycloak_cookie_2)

        assert response.status_code == HTTPStatus.OK

        # assert that we are logged in
        assert re.search(sp_message, response.text) is not None

        # User is logged in on SP1

        # Attempt to perform login on SP2

        response = req.access_sp_ws_fed(logger, s, header, sp2_ip, sp2_port,
                                        sp2_scheme, sp2_path)

        session_cookie = response.cookies

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp2_ip, port=sp2_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp,
                                       {**keycloak_cookie_2})

        # Assert that the client is not authorized to access SP2
        assert response.status_code == HTTPStatus.FORBIDDEN

        assert re.search(sp2_message, response.text) is not None
Exemple #14
0
    def test_security_broker_fuzzing(self, settings):
        """
        This test respects the following use-case:
        - we simulate a login using an external IDP; in this case the external IDP generates a token that is transmitted
        to the broker IDP and used further to allow access to the service provider for the correctly authenticated user
        - for the tests, we fuzz one or more of the parameters 'wa', 'wtrealm', 'wresult' or 'wctx' from the token sent from the
        external IDP to the broker IDP
        - we check what is the return code of the broker IDP when sending the fuzzed token
        - afterwards, we check if Keycloak is still working properly by doing a login and a logout
        Repeat the previous steps an unlimited number of times
        - as output we expect to receive 400 and 414 from Keycloak and for the login and logout to be done
        successfully
        :param settings:
        :return:
        """

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_test_realm = settings["idp"]["test_realm"]["name"]
        idp_form_id = settings["idp"]["login_form_update"]
        idp_broker = settings["idp"]["wsfed_broker"]

        # a login WSFED query
        query = "wa=wsignin1.0&" \
                "wreply=http%3A%2F%2F127.0.0.1%3A7000%2Fj_spring_fediz_security_check&" \
                "wtrealm=sp_wsfed1&" \
                "wct=2018-07-10T14%3A43%3A45.921Z&" \
                "wctx=48022b8c-9b80-4446-8487-f94b24439f44"

        initial_redirect_url = "{scheme}://{ip}:{port}/auth/realms/{realm}/protocol/wsfed?{query}".format(
            scheme=idp_scheme,
            ip=idp_ip,
            port=idp_port,
            realm=idp_test_realm,
            query=query)

        # follow the login flow up to the moment that the external IDP sends the reply (containing the wsfed fields)
        # to the broker
        #for i in range(0,1):
        while True:

            s = Session()

            # Service provider settings
            sp = settings["sps_wsfed"][0]
            sp_ip = sp["ip"]
            sp_port = sp["port"]
            sp_scheme = sp["http_scheme"]
            sp_path = sp["path"]
            sp_message = sp["logged_in_message"]

            # Identity provider settings
            # IDP broker
            idp_ip = settings["idp"]["ip"]
            idp_port = settings["idp"]["port"]
            idp_scheme = settings["idp"]["http_scheme"]
            idp_broker = settings["idp"]["wsfed_broker"]

            idp_client_id = settings["idp"]["master_realm"]["client_id"]
            idp_realm_id = settings["idp"]["master_realm"]["name"]
            idp_realm_test = settings["idp"]["test_realm"]["name"]
            idp_master_username = settings["idp"]["master_realm"]["username"]
            idp_master_password = settings["idp"]["master_realm"]["password"]

            idp_username = settings["idp_external"]["test_realm"]["username"]
            idp_password = settings["idp_external"]["test_realm"]["password"]

            # IDP external
            idp2_ip = settings["idp_external"]["ip"]
            idp2_port = settings["idp_external"]["port"]
            idp2_scheme = settings["idp_external"]["http_scheme"]
            idp2_client_id = settings["idp_external"]["master_realm"][
                "client_id"]
            idp2_realm_id = settings["idp_external"]["master_realm"]["name"]
            idp2_realm_test = settings["idp_external"]["test_realm"]["name"]
            idp2_master_username = settings["idp_external"]["master_realm"][
                "username"]
            idp2_master_password = settings["idp_external"]["master_realm"][
                "password"]

            keycloak_login_form_id = settings["idp"]["login_form_id"]

            # Common header for all the requests
            header = req.get_header()

            response = req.redirect_to_idp(logger, s, initial_redirect_url,
                                           header, None)
            keycloak_cookie = response.cookies

            if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers[
                    'WWW-Authenticate'] == 'Negotiate':
                response = req.kerberos_form_fallback(logger, s, response,
                                                      header,
                                                      {**keycloak_cookie})

            # In the login page we can choose to login with the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')

            div = soup.find("div", {"id": "kc-social-providers"})

            assert div is not None

            # we can have several idp external; choose the one needed for the test
            all_li = div.find_all('li')
            for li in all_li:
                if li.span.text == idp_broker:
                    external_idp_url = "{scheme}://{ip}:{port}".format(
                        scheme=idp_scheme, ip=idp_ip,
                        port=idp_port) + li.a['href']

            assert external_idp_url is not None

            # Select to login with the external IDP
            req_choose_external_idp = Request(
                method='GET',
                url="{url}".format(url=external_idp_url),
                headers=header,
                cookies=keycloak_cookie)

            prepared_request = req_choose_external_idp.prepare()

            req.log_request(logger, req_choose_external_idp)
            response = s.send(prepared_request, allow_redirects=False)
            logger.debug(response.status_code)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            params = {}
            for input in inputs:
                params[input.get('name')] = input.get('value')

            # Redirect to external IDP
            req_redirect_external_idp = Request(
                method=method_form,
                url="{url}".format(url=url_form),
                params=params,
                headers=header)

            referer_url = url_form

            prepared_request = req_redirect_external_idp.prepare()

            req.log_request(logger, req_redirect_external_idp)
            response = s.send(prepared_request, allow_redirects=False)
            logger.debug(response.status_code)

            keycloak_cookie2 = response.cookies

            soup = BeautifulSoup(response.content, 'html.parser')

            form = soup.find("form", {"id": keycloak_login_form_id})

            assert form is not None

            url_form = form.get('action')
            method_form = form.get('method')
            inputs = form.find_all('input')

            input_name = []
            for input in inputs:
                input_name.append(input.get('name'))

            assert "username" in input_name
            assert "password" in input_name

            credentials_data = {}
            credentials_data["username"] = idp_username
            credentials_data["password"] = idp_password

            # Authenticate to the external IDP
            response = req.send_credentials_to_idp(logger, s, header, idp2_ip,
                                                   idp2_port, referer_url,
                                                   url_form, credentials_data,
                                                   {**keycloak_cookie2},
                                                   method_form)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            try:
                ## we fuzz the values of the token
                params = ['wa', 'wtrealm', 'wresult', 'wctx']
                # choose what parameters are going to be fuzzed
                for i in range(0, len(params)):
                    random.seed(
                        calendar.timegm(time.gmtime()) +
                        random.randint(0, 1000))
                    choice = random.random()
                    if choice > 0.5:  # we fuzz the parameter
                        fuzz_value = fuzz.get_fuzzed_value(
                            logger, token[params[i]])
                        token[params[i]] = fuzz_value
            except Exception as e:
                print("There is a problem with the fuzzer: {e}".format(e=e))
                logger.info(
                    "There is a problem with the fuzzer: {e}".format(e=e))

            logger.info(
                "Fuzzed token sent to the broker is {t}".format(t=token))

            req_token_from_external_idp = Request(
                method=method_form,
                url="{url}".format(url=url_form),
                data=token,
                cookies=keycloak_cookie,
                headers=header)

            prepared_request = req_token_from_external_idp.prepare()

            req.log_request(logger, req_token_from_external_idp)
            response = s.send(prepared_request, allow_redirects=False)

            # log what status code we get from the broker
            logger.info(response.status_code)

            # check that Keycloak is up there running and able to answer to requests
            # run the wsfed login test
            s = Session()

            response = req.access_sp_ws_fed(logger, s, header, sp_ip, sp_port,
                                            sp_scheme, sp_path)

            session_cookie = response.cookies

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp_ip,
                                                       port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url,
                                           header_redirect_idp, session_cookie)

            keycloak_cookie = response.cookies

            if response.status_code == HTTPStatus.UNAUTHORIZED and response.headers[
                    'WWW-Authenticate'] == 'Negotiate':
                response = req.kerberos_form_fallback(logger, s, response,
                                                      header, {
                                                          **keycloak_cookie,
                                                          **session_cookie
                                                      })

            # In the login page we can choose to login with the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')

            div = soup.find("div", {"id": "kc-social-providers"})

            assert div is not None

            # we can have several idp external; choose the one needed for the test
            all_li = div.find_all('li')
            for li in all_li:
                if li.span.text == idp_broker:
                    external_idp_url = "{scheme}://{ip}:{port}".format(
                        scheme=idp_scheme, ip=idp_ip,
                        port=idp_port) + li.a['href']

            assert external_idp_url is not None

            # Select to login with the external IDP
            req_choose_external_idp = Request(
                method='GET',
                url="{url}".format(url=external_idp_url),
                headers=header,
                cookies=keycloak_cookie)

            prepared_request = req_choose_external_idp.prepare()

            log_request(logger, req_choose_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            logger.debug(response.status_code)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            params = {}
            for input in inputs:
                params[input.get('name')] = input.get('value')

            header_redirect_external_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp2_ip,
                                                       port=idp2_port),
                'Referer': "{ip}:{port}".format(ip=idp_ip, port=idp_port)
            }

            # Redirect to external IDP
            if idp_broker == "cloudtrust_saml":
                req_redirect_external_idp = Request(
                    method=method_form,
                    url="{url}".format(url=url_form),
                    data=params,
                    headers=header_redirect_external_idp)
            else:
                req_redirect_external_idp = Request(
                    method=method_form,
                    url="{url}".format(url=url_form),
                    params=params,
                    headers=header_redirect_external_idp)

            referer_url = url_form

            prepared_request = req_redirect_external_idp.prepare()

            log_request(logger, req_redirect_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            logger.debug(response.status_code)

            # if we have an identity provider saml, we do an extra redirect
            if idp_broker == "cloudtrust_saml":
                redirect_url = response.headers['Location']
                keycloak_cookie2 = response.cookies
                response = req.redirect_to_idp(logger, s, redirect_url, header,
                                               keycloak_cookie2)
            else:
                keycloak_cookie2 = response.cookies

            soup = BeautifulSoup(response.content, 'html.parser')

            form = soup.find("form", {"id": keycloak_login_form_id})

            assert form is not None

            url_form = form.get('action')
            method_form = form.get('method')
            inputs = form.find_all('input')

            input_name = []
            for input in inputs:
                input_name.append(input.get('name'))

            assert "username" in input_name
            assert "password" in input_name

            credentials_data = {}
            credentials_data["username"] = idp_username
            credentials_data["password"] = idp_password

            # Authenticate to the external IDP
            response = req.send_credentials_to_idp(logger, s, header, idp2_ip,
                                                   idp2_port, referer_url,
                                                   url_form, credentials_data,
                                                   {
                                                       **keycloak_cookie2,
                                                       **session_cookie
                                                   }, method_form)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            req_token_from_external_idp = Request(
                method=method_form,
                url="{url}".format(url=url_form),
                data=token,
                cookies=keycloak_cookie,
                headers=header)

            prepared_request = req_token_from_external_idp.prepare()

            log_request(logger, req_token_from_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            logger.debug(response.status_code)

            if response.status_code == HTTPStatus.FOUND:
                new_cookie = response.cookies
                redirect_url = response.headers['Location']
                response = req.redirect_to_idp(logger, s, redirect_url, header,
                                               {
                                                   **keycloak_cookie,
                                                   **new_cookie
                                               })
                response = req.broker_fill_in_form(logger, s, response, header,
                                                   keycloak_cookie, new_cookie,
                                                   idp_broker, idp_form_id)

            # Get the token from the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            # Access SP with the token
            (response, sp_cookie) = req.access_sp_with_token(
                logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme,
                idp_ip, idp_port, method_form, url_form, token, session_cookie,
                keycloak_cookie2)

            assert response.status_code == HTTPStatus.OK

            # assert that we are logged in
            assert re.search(sp_message, response.text) is not None
            logger.info("Login returned a {code} status code".format(
                code=response.status_code))

            # cleanup: remove the open sessions of the test user from the broker IDP and external IDP

            # remove the sessions from broker IDP
            # first, obtain the id of the user
            user_repr = req.get_user(s, logger, idp_ip, idp_port, idp_scheme,
                                     idp_master_username, idp_master_password,
                                     idp_client_id, idp_realm_id,
                                     idp_realm_test, idp_username)

            user_id = json.loads(user_repr)[0]['id']

            # remove the open sessions
            status_code = req.remove_user_sessions(s, logger, idp_ip, idp_port,
                                                   idp_scheme,
                                                   idp_master_username,
                                                   idp_master_password,
                                                   idp_client_id, idp_realm_id,
                                                   idp_realm_test, user_id)

            assert status_code == HTTPStatus.NO_CONTENT

            # remove the sessions from the external IDP
            # first, obtain the id of the user
            user_repr = req.get_user(s, logger, idp2_ip, idp2_port,
                                     idp2_scheme, idp2_master_username,
                                     idp2_master_password, idp2_client_id,
                                     idp2_realm_id, idp2_realm_test,
                                     idp_username)

            user_id = json.loads(user_repr)[0]['id']

            # remove the open sessions
            status_code = req.remove_user_sessions(
                s, logger, idp2_ip, idp2_port, idp2_scheme,
                idp2_master_username, idp2_master_password, idp2_client_id,
                idp2_realm_id, idp2_realm_test, user_id)

            assert status_code == HTTPStatus.NO_CONTENT
Exemple #15
0
    def test_CT_TC_WS_FED_BROKER_ACCESS_CONTROL_RBAC_KO_IDP_initiated(
            self, settings):
        """
        Scenario: User logs in to the IDP. He then accesses SP1 where he has the appropriate role.
        Same user tries to log in to SP2, that he is not authorized to access. He should receive an
        error message saying he has not the authorization.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_wsfed"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Service provider 2 settings
        sp2 = settings["sps_wsfed"][2]
        sp2_ip = sp2["ip"]
        sp2_port = sp2["port"]
        sp2_scheme = sp2["http_scheme"]
        sp2_path = sp2["path"]
        sp2_message = settings["idp"]["not_authorized_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_test_realm = settings["idp"]["test_realm"]["name"]
        idp_path = "auth/realms/{realm}/account".format(realm=idp_test_realm)
        idp_message = settings["idp"]["logged_in_message"]
        idp_broker = settings["idp"]["wsfed_broker"]
        idp_form_id = settings["idp"]["login_form_update"]

        idp_username = settings["idp_external"]["test_realm"]["username"]
        idp_password = settings["idp_external"]["test_realm"]["password"]

        idp2_ip = settings["idp_external"]["ip"]
        idp2_port = settings["idp_external"]["port"]
        idp2_scheme = settings["idp_external"]["http_scheme"]

        idp2_external_test_realm = settings["idp_external"]["test_realm"][
            "name"]
        idp2_path = "auth/realms/{realm}/account".format(
            realm=idp2_external_test_realm)
        idp2_message = settings["idp"]["logged_in_message"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        # We check that test works for both types of identity provider
        idp_brokers = [
            settings["idp"]["saml_broker"], settings["idp"]["wsfed_broker"]
        ]

        for idp_broker in idp_brokers:

            # Login to the external IDP
            (oath_cookie, keycloak_cookie3, response) = req.login_external_idp(
                logger, s, header, idp_ip, idp_port, idp_scheme, idp_path,
                idp_username, idp_password, idp2_ip, idp2_port, idp_broker,
                idp_form_id)

            assert response.status_code == HTTPStatus.OK

            # Assert we are logged in
            assert re.search(idp_message, response.text) is not None

            response = req.access_sp_ws_fed(logger, s, header, sp_ip, sp_port,
                                            sp_scheme, sp_path)

            # store the cookie received from keycloak
            keycloak_cookie5 = response.cookies

            assert response.status_code == HTTPStatus.FOUND

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp_ip,
                                                       port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url,
                                           header_redirect_idp, {
                                               **keycloak_cookie5,
                                               **keycloak_cookie3
                                           })

            assert response.status_code == HTTPStatus.OK

            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            # Get the token (SAML response) from the broker identity provider
            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            (response, sp_cookie) = req.access_sp_with_token(
                logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme,
                idp_ip, idp_port, method_form, url_form, token,
                keycloak_cookie5, keycloak_cookie5)

            assert response.status_code == HTTPStatus.OK

            # assert that we are logged in
            assert re.search(sp_message, response.text) is not None

            # User can access SP1

            # Attempt to access SP2

            response = req.access_sp_ws_fed(logger, s, header, sp2_ip,
                                            sp2_port, sp2_scheme, sp2_path)

            session_cookie = response.cookies

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp_ip,
                                                       port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp2_ip, port=sp2_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url,
                                           header_redirect_idp,
                                           {**keycloak_cookie3})

            # Assert that the client is not authorized to access SP2
            assert response.status_code == HTTPStatus.FORBIDDEN

            assert re.search(sp2_message, response.text) is not None
    def test_CT_TC_WS_FED_SSO_FORM_SIMPLE_IDP_initiated(self, settings):
        """
        Test the CT_TC_SAML_SSO_FORM_SIMPLE use case with the IDP-initiated flow, i.e. the user logs in keycloak,
        the identity provider (IDP), and then accesses the application, which is a service provider (SP).
        The application redirect towards keycloak to obtain the WSFED token.
        The token contains builtin and external claims.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_wsfed"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_test_realm = settings["idp"]["test_realm"]["name"]
        idp_path = "auth/realms/{realm}/account".format(realm=idp_test_realm)
        idp_message = settings["idp"]["logged_in_message"]

        idp_username = settings["idp"]["test_realm"]["username"]
        idp_password = settings["idp"]["test_realm"]["password"]
        idp_attr_name = settings["idp"]["test_realm"]["attr_name"]
        idp_attr_tag = settings["idp"]["test_realm"]["attr_xml_elem"]
        idp_attr_name_external = settings["idp"]["test_realm"][
            "external_attr_name"]

        # Common header for all the requests
        header = req.get_header()

        (oath_cookie, keycloak_cookie, keycloak_cookie2,
         response) = req.login_idp(logger, s, header, idp_ip, idp_port,
                                   idp_scheme, idp_path, idp_username,
                                   idp_password)

        assert response.status_code == HTTPStatus.OK

        # Assert we are logged in
        assert re.search(idp_message, response.text) is not None

        response = req.access_sp_ws_fed(logger, s, header, sp_ip, sp_port,
                                        sp_scheme, sp_path)

        # store the cookie received from keycloak
        session_cookie = response.cookies

        assert response.status_code == HTTPStatus.FOUND

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp,
                                       {**keycloak_cookie2})

        assert response.status_code == HTTPStatus.OK

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the saml response from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name)
        # assert that the IDP added the location attribute in the token
        assert re.search(val, token['wresult']) is not None

        # assert that the external claim is also in the token
        val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name_external)
        assert re.search(val, token['wresult']) is not None

        (response, sp_cookie) = req.access_sp_with_token(
            logger,
            s,
            header,
            sp_ip,
            sp_port,
            sp_scheme,
            idp_scheme,
            idp_ip,
            idp_port,
            method_form,
            url_form,
            token,
            session_cookie,
            keycloak_cookie2,
        )

        assert response.status_code == HTTPStatus.OK

        assert re.search(sp_message, response.text) is not None
Exemple #17
0
    def test_CT_TC_WS_FED_BROKER_ACCESS_CONTROL_RBAC_KO_SP_initiated(
            self, settings):
        """
        Scenario: User logs in to SP1 where he has the appropriate role.
        Same user tries to log in to SP2, SP that he is not authorized to access. He should receive an
        error message saying he has not the authorization.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_wsfed"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Service provider 2 settings
        sp2 = settings["sps_wsfed"][2]
        sp2_ip = sp2["ip"]
        sp2_port = sp2["port"]
        sp2_scheme = sp2["http_scheme"]
        sp2_path = sp2["path"]
        sp2_message = settings["idp"]["not_authorized_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]
        idp_broker = settings["idp"]["wsfed_broker"]
        idp_form_id = settings["idp"]["login_form_update"]

        idp_username = settings["idp_external"]["test_realm"]["username"]
        idp_password = settings["idp_external"]["test_realm"]["password"]

        idp2_ip = settings["idp_external"]["ip"]
        idp2_port = settings["idp_external"]["port"]
        idp2_scheme = settings["idp_external"]["http_scheme"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        # We check that test works for both types of identity provider
        idp_brokers = [
            settings["idp"]["saml_broker"], settings["idp"]["wsfed_broker"]
        ]

        for idp_broker in idp_brokers:

            response = req.access_sp_ws_fed(logger, s, header, sp_ip, sp_port,
                                            sp_scheme, sp_path)

            session_cookie = response.cookies

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp_ip,
                                                       port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url,
                                           header_redirect_idp, session_cookie)

            keycloak_cookie = response.cookies

            # In the login page we can choose to login with the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')

            div = soup.find("div", {"id": "kc-social-providers"})

            assert div is not None

            # we can have several idp external; choose the one needed for the test
            all_li = div.find_all('li')
            for li in all_li:
                if li.span.text == idp_broker:
                    external_idp_url = "{scheme}://{ip}:{port}".format(
                        scheme=idp_scheme, ip=idp_ip,
                        port=idp_port) + li.a['href']

            assert external_idp_url is not None

            # Select to login with the external IDP
            req_choose_external_idp = Request(
                method='GET',
                url="{url}".format(url=external_idp_url),
                headers=header,
                cookies=keycloak_cookie)

            prepared_request = req_choose_external_idp.prepare()

            log_request(logger, req_choose_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            logger.debug(response.status_code)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            params = {}
            for input in inputs:
                params[input.get('name')] = input.get('value')

            header_redirect_external_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp2_ip,
                                                       port=idp2_port),
                'Referer': "{ip}:{port}".format(ip=idp_ip, port=idp_port)
            }

            # Redirect to external IDP
            if idp_broker == "cloudtrust_saml":
                req_redirect_external_idp = Request(
                    method=method_form,
                    url="{url}".format(url=url_form),
                    data=params,
                    headers=header_redirect_external_idp)
            else:
                req_redirect_external_idp = Request(
                    method=method_form,
                    url="{url}".format(url=url_form),
                    params=params,
                    headers=header_redirect_external_idp)

            # url_parts = list(urlparse.urlparse(url_form))
            # query = dict(urlparse.parse_qsl(url_parts[4]))
            # query.update(params)
            # url_parts[4] = urlencode(query)
            # referer_url = urlparse.urlunparse(url_parts)
            referer_url = url_form

            prepared_request = req_redirect_external_idp.prepare()

            log_request(logger, req_redirect_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            logger.debug(response.status_code)

            # if we have an identity provider saml, we do an extra redirect
            if idp_broker == "cloudtrust_saml":
                redirect_url = response.headers['Location']
                keycloak_cookie2 = response.cookies
                response = req.redirect_to_idp(logger, s, redirect_url, header,
                                               keycloak_cookie2)
            else:
                keycloak_cookie2 = response.cookies

            soup = BeautifulSoup(response.content, 'html.parser')

            form = soup.find("form", {"id": keycloak_login_form_id})

            assert form is not None

            url_form = form.get('action')
            method_form = form.get('method')
            inputs = form.find_all('input')

            input_name = []
            for input in inputs:
                input_name.append(input.get('name'))

            assert "username" in input_name
            assert "password" in input_name

            credentials_data = {}
            credentials_data["username"] = idp_username
            credentials_data["password"] = idp_password

            # Authenticate to the external IDP
            response = req.send_credentials_to_idp(logger, s, header, idp2_ip,
                                                   idp2_port, referer_url,
                                                   url_form, credentials_data,
                                                   {
                                                       **keycloak_cookie2,
                                                       **session_cookie
                                                   }, method_form)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            req_token_from_external_idp = Request(
                method=method_form,
                url="{url}".format(url=url_form),
                data=token,
                cookies=keycloak_cookie,
                headers=header)

            prepared_request = req_token_from_external_idp.prepare()

            log_request(logger, req_token_from_external_idp)

            response = s.send(prepared_request,
                              verify=False,
                              allow_redirects=False)

            if response.status_code == HTTPStatus.FOUND:
                new_cookie = response.cookies
                redirect_url = response.headers['Location']
                response = req.redirect_to_idp(logger, s, redirect_url, header,
                                               {
                                                   **keycloak_cookie,
                                                   **new_cookie
                                               })
                response = req.broker_fill_in_form(logger, s, response, header,
                                                   keycloak_cookie, new_cookie,
                                                   idp_broker, idp_form_id)

            keycloak_cookie3 = response.cookies

            logger.debug(response.status_code)

            # Get the token from the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            # Access SP with the token
            (response, sp_cookie) = req.access_sp_with_token(
                logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme,
                idp_ip, idp_port, method_form, url_form, token, session_cookie,
                keycloak_cookie2)

            assert response.status_code == HTTPStatus.OK

            # assert that we are logged in
            assert re.search(sp_message, response.text) is not None

            # User is logged in on SP1

            # Attempt to perform login on SP2

            response = req.access_sp_ws_fed(logger, s, header, sp2_ip,
                                            sp2_port, sp2_scheme, sp2_path)

            session_cookie = response.cookies

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header, 'Host': "{ip}:{port}".format(ip=idp_ip,
                                                       port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp2_ip, port=sp2_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url,
                                           header_redirect_idp,
                                           {**keycloak_cookie3})

            # Assert that the client is not authorized to access SP2
            assert response.status_code == HTTPStatus.FORBIDDEN

            assert re.search(sp2_message, response.text) is not None
Exemple #18
0
    def test_CT_TC_SAML_IDP_LOGOUT_SIMPLE(self, settings, login_sso_form):
        """
        Test the CT_TC_SAML_IDP_LOGOUT_SIMPLE use case with the SP-initiated flow, i.e. the user that accessed the SP
        asks to be logged out. This will trigger the logout to be performed on the IDP side and the user will
        be able to see the "You're logged out" page.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_saml"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_logout_path = sp["logout_path"]
        sp_message = sp["logged_out_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]

        # Common header for all the requests
        header = req.get_header()

        # Perform login using the fixture login_sso_form
        sp_cookie, keycloak_cookie = login_sso_form

        # User is logged in

        # Access to the SP logout page
        header_sp_logout_page = {
            **header, 'Host':
            "{ip}:{port}".format(ip=sp_ip, port=sp_port),
            'Referer':
            "{scheme}://{ip}:{port}".format(scheme=sp_scheme,
                                            ip=sp_ip,
                                            port=sp_port)
        }

        req_get_sp_logout_page = Request(
            method='GET',
            url="{scheme}://{ip}:{port}/{path}".format(scheme=sp_scheme,
                                                       port=sp_port,
                                                       ip=sp_ip,
                                                       path=sp_logout_path),
            headers=header_sp_logout_page,
            cookies=sp_cookie)

        prepared_request = req_get_sp_logout_page.prepare()

        log_request(logger, req_get_sp_logout_page)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        assert response.status_code == HTTPStatus.OK

        # SP redirects me to IDP with a SAML request
        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.body.form
        url_form = form.get('action')
        method_form = form.get('method')
        inputs = form.find_all('input')

        # Do a SAML request to the identity provider
        saml_request = {}
        for input in inputs:
            saml_request[input.get('name')] = input.get('value')

        header_redirect_idp = {
            **header,
            'Host':
            "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer':
            "{scheme}://{ip}:{port}/{path}".format(scheme=sp_scheme,
                                                   ip=sp_ip,
                                                   port=sp_port,
                                                   path=sp_logout_path),
        }

        req_idp_saml_request = Request(method=method_form,
                                       url="{url}".format(url=url_form),
                                       data=saml_request,
                                       headers=header_redirect_idp,
                                       cookies={**keycloak_cookie})

        prepared_request = req_idp_saml_request.prepare()

        log_request(logger, req_idp_saml_request)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        assert response.status_code == HTTPStatus.OK

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        saml_response = {}
        for input in inputs:
            saml_response[input.get('name')] = input.get('value')

        header_idp_saml_response = {
            **header,
            'Host':
            "{ip}:{port}".format(ip=sp_ip, port=sp_port),
            'Referer':
            "{scheme}://{ip}:{port}".format(scheme=idp_scheme,
                                            ip=idp_ip,
                                            port=idp_port),
        }

        # Provide to the SP the SAML response
        req_sp_saml_response = Request(method=method_form,
                                       url="{url}".format(url=url_form),
                                       data=saml_response,
                                       headers=header_idp_saml_response)

        prepared_request = req_sp_saml_response.prepare()

        log_request(logger, req_sp_saml_response)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        url_sp = response.headers['Location']

        req_logout = Request(method='GET',
                             url="{url}".format(url=url_sp),
                             headers=header_idp_saml_response)

        prepared_request = req_logout.prepare()

        log_request(logger, req_logout)

        response = s.send(prepared_request, verify=False)

        logger.debug(response.status_code)

        assert response.status_code == HTTPStatus.OK

        # Assert the logout page is displayed
        assert re.search(sp_message, response.text) is not None
Exemple #19
0
    def test_CT_TC_SAML_SSO_BROKER_SIMPLE_SP_initiated(self, settings):
        """
        Test the CT_TC_SAML_SSO_BROKER_SIMPLE use case with the SP-initiated flow, i.e. the user accesses the application
        , which is a service provider (SP), that redirects him to the keycloak, the identity provider (IDP).
        The user has to login to keycloak which will give him the SAML token. The token will give him access to the
        application.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_saml"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]

        idp_username = settings["idp_external"]["test_realm"]["username"]
        idp_password = settings["idp_external"]["test_realm"]["password"]
        idp_broker = settings["idp"]["saml_broker"]
        idp_form_id = settings["idp"]["login_form_update"]

        idp2_ip = settings["idp_external"]["ip"]
        idp2_port = settings["idp_external"]["port"]
        idp2_scheme = settings["idp_external"]["http_scheme"]

        idp_attr_name = settings["idp"]["test_realm"]["attr_name"]
        idp_attr_name_external = settings["idp"]["test_realm"]["external_attr_name"]
        idp_attr_tag = settings["idp"]["test_realm"]["attr_xml_elem"]

        idp_attr_name_broker = settings["idp_external"]["test_realm"]["attr_name"]
        idp_attr_tag_broker = settings["idp_external"]["test_realm"]["attr_xml_elem"]



        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        # We check that test works for both types of identity provider
        idp_brokers = [settings["idp"]["saml_broker"], settings["idp"]["wsfed_broker"]]

        for idp_broker in idp_brokers:

            (session_cookie, response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port, sp_scheme, sp_path,
                                                                            idp_ip, idp_port)

            assert response.status_code == HTTPStatus.FOUND

            # store the cookie received from keycloak
            keycloak_cookie = response.cookies

            redirect_url = response.headers['Location']

            header_redirect_idp = {
                **header,
                'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
                'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
            }

            response = req.redirect_to_idp(logger, s, redirect_url, header_redirect_idp, keycloak_cookie)

            # In the login page we can choose to login with the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')

            div = soup.find("div", {"id": "kc-social-providers"})

            assert div is not None

            # we can have several idp external; choose the one needed for the test
            all_li = div.find_all('li')
            for li in all_li:
                if li.span.text == idp_broker:
                    external_idp_url = "{scheme}://{ip}:{port}".format(scheme=idp_scheme, ip=idp_ip, port=idp_port) + li.a['href']

            assert external_idp_url is not None

            # Select to login with the external IDP
            req_choose_external_idp = Request(
                method='GET',
                url="{url}".format(url=external_idp_url),
                headers=header,
                cookies=keycloak_cookie
            )

            prepared_request = req_choose_external_idp.prepare()

            log_request(logger, req_choose_external_idp)

            response = s.send(prepared_request, verify=False, allow_redirects=False)

            logger.debug(response.status_code)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the external IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            params = {}
            for input in inputs:
                params[input.get('name')] = input.get('value')

            header_redirect_external_idp = {
                **header,
                'Host': "{ip}:{port}".format(ip=idp2_ip, port=idp2_port),
                'Referer': "{ip}:{port}".format(ip=idp_ip, port=idp_port)
            }

            # Redirect to external IDP
            if idp_broker == "cloudtrust_saml":
                req_redirect_external_idp = Request(
                    method=method_form,
                    url="{url}".format(url=url_form),
                    data=params,
                    headers=header_redirect_external_idp
                )
            else:
                req_redirect_external_idp = Request(
                    method=method_form,
                    url="{url}".format(url=url_form),
                    params=params,
                    headers=header_redirect_external_idp
                )

            # url_parts = list(urlparse.urlparse(url_form))
            # query = dict(urlparse.parse_qsl(url_parts[4]))
            # query.update(params)
            # url_parts[4] = urlencode(query)
            # referer_url = urlparse.urlunparse(url_parts)
            referer_url = url_form

            prepared_request = req_redirect_external_idp.prepare()

            log_request(logger, req_redirect_external_idp)

            response = s.send(prepared_request, verify=False, allow_redirects=False)

            logger.debug(response.status_code)

            # if we have an identity provider saml, we do an extra redirect
            if idp_broker == "cloudtrust_saml":
                redirect_url = response.headers['Location']
                keycloak_cookie_ext = response.cookies
                response = req.redirect_to_idp(logger, s, redirect_url, header, keycloak_cookie_ext)
            else:
                keycloak_cookie_ext = response.cookies

            soup = BeautifulSoup(response.content, 'html.parser')

            form = soup.find("form", {"id": keycloak_login_form_id})

            assert form is not None

            url_form = form.get('action')
            method_form = form.get('method')
            inputs = form.find_all('input')

            input_name = []
            for input in inputs:
                    input_name.append(input.get('name'))

            assert "username" in input_name
            assert "password" in input_name

            credentials_data = {}
            credentials_data["username"] = idp_username
            credentials_data["password"] = idp_password

            # Authenticate to the external IDP
            response = req.send_credentials_to_idp(logger, s, header, idp2_ip, idp2_port, referer_url, url_form,
                                                   credentials_data, {**session_cookie, **keycloak_cookie_ext}, method_form)

            assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND

            # get the HTTP binding response with the url to the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            req_token_from_external_idp = Request(
                method=method_form,
                url="{url}".format(url=url_form),
                data=token,
                cookies= keycloak_cookie,
                headers=header
            )

            prepared_request = req_token_from_external_idp.prepare()

            log_request(logger, req_token_from_external_idp)

            response = s.send(prepared_request, verify=False, allow_redirects=False)

            logger.debug(response.status_code)

            if response.status_code == HTTPStatus.FOUND:
                new_cookie = response.cookies
                redirect_url = response.headers['Location']
                response = req.redirect_to_idp(logger, s, redirect_url, header, {**keycloak_cookie, **new_cookie})
                response = req.broker_fill_in_form(logger, s, response, header, keycloak_cookie, new_cookie, idp_broker,
                                                   idp_form_id)

            # Get the token (SAML response) from the broker IDP
            soup = BeautifulSoup(response.content, 'html.parser')
            form = soup.body.form

            url_form = form.get('action')
            inputs = form.find_all('input')
            method_form = form.get('method')

            token = {}
            for input in inputs:
                token[input.get('name')] = input.get('value')

            decoded_token = base64.b64decode(token['SAMLResponse']).decode("utf-8")

            val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name)
            # assert that the IDP added the location attribute in the token
            assert re.search(val, decoded_token) is not None

            # assert that the external claim is also in the token
            val = idp_attr_tag + "=\"{v}\"".format(v=idp_attr_name_external)
            assert re.search(val, decoded_token) is not None

            # assert that the claims that come from the external IDP are well in the token
            val = idp_attr_tag_broker + "=\"{v}\"".format(v=idp_attr_name_broker)
            # assert that the IDP added the location attribute in the token
            assert re.search(val, decoded_token) is not None


            # Access SP with the token
            (response, sp_cookie) = req.access_sp_with_token(logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme,
                                                             idp_ip, idp_port, method_form, url_form, token, session_cookie,
                                                             keycloak_cookie)

            assert response.status_code == HTTPStatus.OK

            # assert that we are logged in
            assert re.search(sp_message, response.text) is not None
    def test_CT_TC_SAML_SSO_BROKER_LOGOUT_SIMPLE(self, settings, login_broker_sso_form):
        """
        #TODO:update the description and the comments
        Test the CT_TC_SAML_SSO_FORM_SIMPLE use case with the SP-initiated flow, i.e. the user accesses the application
        , which is a service provider (SP), that redirects him to the keycloak, the identity provider (IDP).
        The user has to login to keycloak which will give him the SAML token. The token will give him access to the
        application.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_saml"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]

        idp_username = settings["idp_external"]["test_realm"]["username"]
        idp_password = settings["idp_external"]["test_realm"]["password"]

        idp2_ip = settings["idp_external"]["ip"]
        idp2_port = settings["idp_external"]["port"]
        idp2_scheme = settings["idp_external"]["http_scheme"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        #sp_cookie, keycloak_cookie = login_broker_sso_form

        # header_sp_reload_page = {
        #     **header,
        #     'Host': "{ip}:{port}".format(ip=sp_ip, port=sp_port),
        #     'Referer': "{scheme}://{ip}:{port}".format(scheme=idp_scheme, ip=idp_ip, port=idp_port)
        # }
        #
        # req_get_sp_login_reload_page = Request(
        #     method='GET',
        #     url="{scheme}://{ip}:{port}/{path}".format(
        #         scheme=sp_scheme,
        #         port=sp_port,
        #         ip=sp_ip,
        #         path=sp_path
        #     ),
        #     headers=header_sp_reload_page,
        #     cookies={**sp_cookie}
        # )
        #
        # prepared_request = req_get_sp_login_reload_page.prepare()
        #
        # req.log_request(logger, req_get_sp_login_reload_page)
        #
        # response = s.send(prepared_request, verify=False, allow_redirects=False)
        #
        # logger.debug(response.status_code)
        #
        # print(response.text)
        # # the user is logged in and refreshing the page will return an OK
        # #assert response.status_code == 200
    def test_CT_TC_SAML_IDP_ACCESS_CONTROL_RBAC_OK_SP_initiated(
            self, settings):
        """
        Scenario: User logs in to SP1 where he has the appropriate role.
        Same user tries to access to SP2, SP that he is authorized to access. He should
        be able to access SP2 without authenticating again.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp = settings["sps_saml"][0]
        sp_ip = sp["ip"]
        sp_port = sp["port"]
        sp_scheme = sp["http_scheme"]
        sp_path = sp["path"]
        sp_message = sp["logged_in_message"]

        # Service provider 2 settings
        sp2 = settings["sps_saml"][1]
        sp2_ip = sp2["ip"]
        sp2_port = sp2["port"]
        sp2_scheme = sp2["http_scheme"]
        sp2_path = sp2["path"]
        sp2_message = sp2["logged_in_message"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]

        idp_username = settings["idp"]["test_realm"]["username"]
        idp_password = settings["idp"]["test_realm"]["password"]

        keycloak_login_form_id = settings["idp"]["login_form_id"]

        # Common header for all the requests
        header = req.get_header()

        # Perform login to SP1

        (session_cookie,
         response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port,
                                        sp_scheme, sp_path, idp_ip, idp_port)

        assert response.status_code == HTTPStatus.FOUND

        # store the cookie received from keycloak
        keycloak_cookie = response.cookies

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp_ip, port=sp_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp, keycloak_cookie)

        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.find("form", {"id": keycloak_login_form_id})

        assert form is not None

        url_form = form.get('action')
        method_form = form.get('method')

        inputs = form.find_all('input')

        input_name = []
        for input in inputs:
            input_name.append(input.get('name'))

        assert "username" in input_name
        assert "password" in input_name

        # Simulate the login to the identity provider by providing the credentials
        credentials_data = {}
        credentials_data["username"] = idp_username
        credentials_data["password"] = idp_password

        response = req.send_credentials_to_idp(logger, s, header, idp_ip,
                                               idp_port, redirect_url,
                                               url_form, credentials_data,
                                               keycloak_cookie, method_form)

        assert response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.FOUND  #or response.status_code == 303 or response.status_code == 307

        keycloak_cookie_2 = response.cookies

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        (response, sp_cookie) = req.access_sp_with_token(
            logger, s, header, sp_ip, sp_port, sp_scheme, idp_scheme, idp_ip,
            idp_port, method_form, url_form, token, session_cookie,
            keycloak_cookie_2)

        assert response.status_code == HTTPStatus.OK

        # assert that we are logged in
        assert re.search(sp_message, response.text) is not None

        # User is logged in on SP1

        # Attempt to perform login on SP2

        (session_cookie,
         response) = req.access_sp_saml(logger, s, header, sp2_ip, sp2_port,
                                        sp2_scheme, sp2_path, idp_ip, idp_port)

        session_cookie2 = response.cookies

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp2_ip, port=sp2_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp, {
                                           **session_cookie2,
                                           **keycloak_cookie_2
                                       })

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        (response, sp2_cookie) = req.access_sp_with_token(
            logger, s, header, sp2_ip, sp2_port, sp2_scheme, idp_scheme,
            idp_ip, idp_port, method_form, url_form, token, session_cookie,
            session_cookie2)

        assert response.status_code == HTTPStatus.OK

        assert re.search(sp2_message, response.text) is not None
Exemple #22
0
    def test_CT_TC_SAML_IDP_LOGOUT_PERIMETRIC(self, settings, login_sso_form):
        """
        Scenario: user is logged in on several SPs.
        The user logs out of one SP. Access to all the other SPs should require a new log in.
        :param settings:
        :return:
        """

        s = Session()

        # Service provider settings
        sp1 = settings["sps_saml"][0]
        sp_ip = sp1["ip"]
        sp_port = sp1["port"]
        sp_scheme = sp1["http_scheme"]
        sp_path = sp1["path"]
        sp_logout_path = sp1["logout_path"]
        sp_message = sp1["logged_out_message"]

        sp2 = settings["sps_saml"][1]
        sp2_ip = sp2["ip"]
        sp2_port = sp2["port"]
        sp2_scheme = sp2["http_scheme"]
        sp2_path = sp2["path"]

        # Identity provider settings
        idp_ip = settings["idp"]["ip"]
        idp_port = settings["idp"]["port"]
        idp_scheme = settings["idp"]["http_scheme"]

        # Common header for all the requests
        header = req.get_header()

        # Perform login using the fixture login_sso_form
        sp_cookie, keycloak_cookie = login_sso_form

        # User is logged in on SP1

        # Perform login on  SP2

        (session_cookie,
         response) = req.access_sp_saml(logger, s, header, sp2_ip, sp2_port,
                                        sp2_scheme, sp2_path, idp_ip, idp_port)

        session_cookie2 = response.cookies

        redirect_url = response.headers['Location']

        header_redirect_idp = {
            **header, 'Host': "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer': "{ip}:{port}".format(ip=sp2_ip, port=sp2_port)
        }

        response = req.redirect_to_idp(logger, s, redirect_url,
                                       header_redirect_idp, {
                                           **keycloak_cookie,
                                           **session_cookie2
                                       })

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        (response, sp2_cookie) = req.access_sp_with_token(
            logger,
            s,
            header,
            sp2_ip,
            sp2_port,
            sp2_scheme,
            idp_scheme,
            idp_ip,
            idp_port,
            method_form,
            url_form,
            token,
            session_cookie,
            session_cookie2,
        )

        # req_get_sp_login_reload_page = Request(
        #     method='GET',
        #     url="{scheme}://{ip}:{port}/{path}".format(
        #         scheme=sp2_scheme,
        #         port=sp2_port,
        #         ip=sp2_ip,
        #         path=sp2_path
        #     ),
        #     headers=header_sp2_reload_page,
        #     cookies={**session_cookie}
        # )
        #
        # prepared_request = req_get_sp_login_reload_page.prepare()
        #
        # logger.debug(
        #     json.dumps(
        #         prepared_request_to_json(req_get_sp_login_reload_page),
        #         sort_keys=True,
        #         indent=4,
        #         separators=(',', ': ')
        #     )
        # )
        #
        # response = s.send(prepared_request, verify=False, allow_redirects=False)
        #
        # logger.debug(response.status_code)
        #
        # # the user is logged in and refreshing the page will return an OK
        # assert response.status_code == 200

        # User is now logged in on both applications: SP1 and SP2

        # Logout from the first applications

        header_sp_logout_page = {
            **header, 'Host':
            "{ip}:{port}".format(ip=sp_ip, port=sp_port),
            'Referer':
            "{scheme}://{ip}:{port}".format(scheme=sp_scheme,
                                            ip=sp_ip,
                                            port=sp_port)
        }

        req_get_sp_logout_page = Request(
            method='GET',
            url="{scheme}://{ip}:{port}/{path}".format(scheme=sp_scheme,
                                                       port=sp_port,
                                                       ip=sp_ip,
                                                       path=sp_logout_path),
            headers=header_sp_logout_page,
            cookies={**sp_cookie})

        prepared_request = req_get_sp_logout_page.prepare()

        log_request(logger, req_get_sp_logout_page)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        # new session cookie
        session_cookie2 = response.cookies

        # SP redirects me to IDP with a SAML request
        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.body.form
        url_form = form.get('action')
        method_form = form.get('method')
        inputs = form.find_all('input')

        # Do a SAML request to the identity provider
        saml_request = {}
        for input in inputs:
            saml_request[input.get('name')] = input.get('value')

        header_redirect_idp = {
            **header,
            'Host':
            "{ip}:{port}".format(ip=idp_ip, port=idp_port),
            'Referer':
            "{scheme}://{ip}:{port}/{path}".format(scheme=sp_scheme,
                                                   ip=sp_ip,
                                                   port=sp_port,
                                                   path=sp_logout_path),
        }

        req_idp_saml_request = Request(method=method_form,
                                       url="{url}".format(url=url_form),
                                       data=saml_request,
                                       headers=header_redirect_idp)

        prepared_request = req_idp_saml_request.prepare()

        log_request(logger, req_idp_saml_request)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        assert response.status_code == HTTPStatus.OK

        soup = BeautifulSoup(response.content, 'html.parser')
        form = soup.body.form

        url_form = form.get('action')
        inputs = form.find_all('input')
        method_form = form.get('method')

        # Get the token (SAML response) from the identity provider
        token = {}
        for input in inputs:
            token[input.get('name')] = input.get('value')

        header_idp_saml_response = {
            **header,
            'Host':
            "{ip}:{port}".format(ip=sp_ip, port=sp_port),
            'Referer':
            "{scheme}://{ip}:{port}".format(scheme=idp_scheme,
                                            ip=idp_ip,
                                            port=idp_port),
        }

        # Provide to the SP the SAML response
        req_sp_saml_response = Request(method=method_form,
                                       url="{url}".format(url=url_form),
                                       data=token,
                                       headers=header_idp_saml_response)

        prepared_request = req_sp_saml_response.prepare()

        log_request(logger, req_sp_saml_response)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        url_sp = response.headers['Location']

        req_logout = Request(method='GET',
                             url="{url}".format(url=url_sp),
                             headers=header_idp_saml_response)

        prepared_request = req_logout.prepare()

        log_request(logger, req_logout)

        response = s.send(prepared_request, verify=False)

        logger.debug(response.status_code)

        assert response.status_code == HTTPStatus.OK

        # Assert the logout page is displayed
        assert re.search(sp_message, response.text) is not None

        # Check that when the user accesses the secured page of SP1 with the old session cookie,
        # he receives a 200 with the SAML request

        header_sp_reload_page = {
            **header, 'Host':
            "{ip}:{port}".format(ip=sp_ip, port=sp_port),
            'Referer':
            "{scheme}://{ip}:{port}".format(scheme=idp_scheme,
                                            ip=idp_ip,
                                            port=idp_port)
        }

        req_get_sp_login_reload_page = Request(
            method='GET',
            url="{scheme}://{ip}:{port}/{path}".format(scheme=sp_scheme,
                                                       port=sp_port,
                                                       ip=sp_ip,
                                                       path=sp_path),
            headers=header_sp_reload_page,
            cookies={**session_cookie})

        prepared_request = req_get_sp_login_reload_page.prepare()

        log_request(logger, req_get_sp_login_reload_page)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        assert response.status_code == HTTPStatus.OK

        # Response should return a form that requests a post with RelayState and SAMLRequest as input
        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.body.form
        inputs = form.find_all('input')

        # Check we get RelayState and SAMLRequest
        input_name = []
        for input in inputs:
            input_name.append(input.get('name'))

        assert "RelayState" in input_name
        assert "SAMLRequest" in input_name

        # Check if the user is logged out from SP2: perform a refresh of the page; we expect to get a 200 with a form
        # containing the SAMLRequest

        header_sp2_reload_page = {
            **header, 'Host':
            "{ip}:{port}".format(ip=sp2_ip, port=sp2_port),
            'Referer':
            "{scheme}://{ip}:{port}".format(scheme=idp_scheme,
                                            ip=idp_ip,
                                            port=idp_port)
        }

        req_get_sp_login_reload_page = Request(
            method='GET',
            url="{scheme}://{ip}:{port}/{path}".format(scheme=sp2_scheme,
                                                       port=sp2_port,
                                                       ip=sp2_ip,
                                                       path=sp2_path),
            headers=header_sp2_reload_page,
            cookies={**session_cookie})

        prepared_request = req_get_sp_login_reload_page.prepare()

        log_request(logger, req_get_sp_login_reload_page)

        response = s.send(prepared_request,
                          verify=False,
                          allow_redirects=False)

        logger.debug(response.status_code)

        assert response.status_code == HTTPStatus.OK

        # Response should return a form that requests a post with RelayState and SAMLRequest as input
        soup = BeautifulSoup(response.content, 'html.parser')

        form = soup.body.form
        inputs = form.find_all('input')

        # Check we get a form with input RelayState and SAMLRequest
        input_name = []
        for input in inputs:
            input_name.append(input.get('name'))

        assert "RelayState" in input_name
        assert "SAMLRequest" in input_name