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
def login_sso_form(settings, pytestconfig): """ Fixture to perform the log in :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" elif standard == "SAML": client = "sps_saml" # 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"] 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 = { 'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 'Accept-Encoding': "gzip, deflate", 'Accept-Language': "en-US,en;q=0.5", 'User-Agent': "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0", 'Connection': "keep-alive", 'Upgrade-Insecure-Requests': "1", } # Perform login if standard == "WSFED": response = req.access_sp_ws_fed(logger, s, header, sp_ip, sp_port, sp_scheme, sp_path) elif standard == "SAML": (cookie1, response) = req.access_sp_saml(logger, s, header, sp_ip, sp_port, sp_scheme, sp_path, idp_ip, idp_port) 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}) 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')) # Simulate the login to the identity provider by providing the credentials credentials_data = {} credentials_data["username"] = idp_username credentials_data["password"] = idp_password if standard == "WSFED": response = req.send_credentials_to_idp(logger, s, header, idp_ip, idp_port, redirect_url, url_form, credentials_data, keycloak_cookie, method_form) elif standard == "SAML": response = req.send_credentials_to_idp(logger, s, header, idp_ip, idp_port, redirect_url, url_form, credentials_data, session_cookie, method_form) 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 IDP token = {} for input in inputs: token[input.get('name')] = input.get('value') if standard == "WSFED": (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) elif standard == "SAML": (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, cookie1, keycloak_cookie_2) return sp_cookie, keycloak_cookie_2
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
def test_CT_TC_WS_FED_IDP_CLAIM_AUG_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 WSFED 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_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_username = settings["idp"]["test_realm"]["username"] idp_password = settings["idp"]["test_realm"]["password"] keycloak_login_form_id = settings["idp"]["login_form_id"] 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() 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') 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_cookie_2, ) 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_WS_FED_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_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"][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 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 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_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 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_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') 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) assert response.status_code == HTTPStatus.OK assert re.search(sp2_message, response.text) is not None
def test_CT_TC_WS_FED_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 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_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"][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 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}) 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_2) assert response.status_code == HTTPStatus.OK assert re.search(sp2_message, response.text) is not None
def test_CT_TC_WS_FED_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 sp_ip = settings["service_provider"]["ip"] sp_port = settings["service_provider"]["port"] sp_scheme = settings["service_provider"]["http_scheme"] sp_path = settings["service_provider"]["path"] sp_message = settings["service_provider"]["logged_in_message"] # Identity provider settings idp_ip = settings["identity_provider"]["ip"] idp_port = settings["identity_provider"]["port"] idp_scheme = settings["identity_provider"]["http_scheme"] idp_username = settings["identity_provider"]["username"] idp_password = settings["identity_provider"]["password"] keycloak_login_form_id = settings["identity_provider"]["login_form_id"] # Common header for all the requests header = { 'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 'Accept-Encoding': "gzip, deflate", 'Accept-Language': "en-US,en;q=0.5", 'User-Agent': "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0", 'Connection': "keep-alive", 'Upgrade-Insecure-Requests': "1", } response = req.access_sp_ws_fed(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(s, redirect_url, header_redirect_idp, session_cookie) 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 # 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(s, header, idp_ip, idp_port, redirect_url, url_form, credentials_data, keycloak_cookie, method_form) # print(response.text) assert response.status_code == 200 or response.status_code == 302 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 SAML response from the identity provider ws_fed_response = {} for input in inputs: ws_fed_response[input.get('name')] = input.get('value') (response, sp_cookie) = req.access_sp_with_token( s, header, sp_ip, sp_port, idp_scheme, idp_ip, idp_port, method_form, url_form, ws_fed_response, session_cookie, keycloak_cookie_2) assert response.status_code == 200 # assert that we are logged in 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
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
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 SAML token. :param settings: :return: """ s = Session() # Service provider settings sp_ip = settings["service_provider"]["ip"] sp_port = settings["service_provider"]["port"] sp_scheme = settings["service_provider"]["http_scheme"] sp_path = settings["service_provider"]["path"] sp_message = settings["service_provider"]["logged_in_message"] # Identity provider settings idp_ip = settings["identity_provider"]["ip"] idp_port = settings["identity_provider"]["port"] idp_scheme = settings["identity_provider"]["http_scheme"] idp_path = settings["identity_provider"]["path"] idp_message = settings["identity_provider"]["logged_in_message"] idp_username = settings["identity_provider"]["username"] idp_password = settings["identity_provider"]["password"] # Common header for all the requests header = { 'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 'Accept-Encoding': "gzip, deflate", 'Accept-Language': "en-US,en;q=0.5", 'User-Agent': "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0", 'Connection': "keep-alive", 'Upgrade-Insecure-Requests': "1", } (oath_cookie, keycloak_cookie, keycloak_cookie2, response) = req.login_idp(s, header, idp_ip, idp_port, idp_scheme, idp_path, idp_username, idp_password) assert response.status_code == 200 # Assert we are logged in assert re.search(idp_message, response.text) is not None response = req.access_sp_ws_fed(s, header, sp_ip, sp_port, sp_scheme, sp_path) # store the cookie received from keycloak session_cookie = response.cookies assert response.status_code == 302 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(s, redirect_url, header_redirect_idp, {**keycloak_cookie2}) assert response.status_code == 200 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 ws_fed_response = {} for input in inputs: ws_fed_response[input.get('name')] = input.get('value') (response, sp_cookie) = req.access_sp_with_token( s, header, sp_ip, sp_port, idp_scheme, idp_ip, idp_port, method_form, url_form, ws_fed_response, session_cookie, keycloak_cookie2) assert response.status_code == 200 assert re.search(sp_message, response.text) is not None
def test_CT_TC_WS_FED_BROKER_SIMPLE_IDP_initiated(self, settings): """ Test the CT_TC_WS_FED_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 redirect towards keycloak to obtain the SAML token. :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_broker = settings["idp"]["saml_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 login 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 re.search(sp_message, response.text) is not None
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
def test_CT_TC_WS_FED_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 WSFED token. The token will give him access to the application. :param settings: :return: """ s = Session() # Service provider settings sps = [settings["sps_wsfed"][0], settings["sps_wsfed"][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() 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 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
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 the other SPs should require a new log in. :param settings: :return: """ s = Session() # Service provider settings sp_ip = settings["service_provider"]["ip"] sp_port = settings["service_provider"]["port"] sp_scheme = settings["service_provider"]["http_scheme"] sp_logout_path = settings["service_provider"]["logout_path"] sp_message = settings["service_provider"]["logged_out_message"] sp_path = settings["service_provider"]["path"] # Service provider 2 settings sp2_ip = settings["service_provider2"]["ip"] sp2_port = settings["service_provider2"]["port"] sp2_scheme = settings["service_provider2"]["http_scheme"] sp2_logout_path = settings["service_provider2"]["logout_path"] sp2_path = settings["service_provider2"]["path"] sp2_message = settings["service_provider2"]["logged_in_message"] # Identity provider settings idp_ip = settings["identity_provider"]["ip"] idp_port = settings["identity_provider"]["port"] idp_scheme = settings["identity_provider"]["http_scheme"] # Common header for all the requests header = { 'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 'Accept-Encoding': "gzip, deflate", 'Accept-Language': "en-US,en;q=0.5", 'User-Agent': "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0", 'Connection': "keep-alive", 'Upgrade-Insecure-Requests': "1", } # 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(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(s, redirect_url, header_redirect_idp, {**keycloak_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') ws_fed_response = {} for input in inputs: ws_fed_response[input.get('name')] = input.get('value') (response, sp2_cookie) = req.access_sp_with_token( s, header, sp2_ip, sp2_port, idp_scheme, idp_ip, idp_port, method_form, url_form, ws_fed_response, 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() logger.debug( json.dumps(prepared_request_to_json(req_get_sp_logout_page), sort_keys=True, indent=4, separators=(',', ': '))) 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() logger.debug( json.dumps(prepared_request_to_json(req_sp_logout_redirect), sort_keys=True, indent=4, separators=(',', ': '))) 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(s, redirect_url, header, sp_cookie) assert response.status_code == 200 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(s, header, sp_ip, sp_port, idp_scheme, idp_ip, idp_port, method_form, url_form, token, sp_cookie, sp_cookie) assert response.status_code == 200 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() 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) # Assert that the refresh page gives a 302 which signals that the user is logged out of SP assert response.status_code == 302 # 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() 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) # Assert that the refresh page gives a 302 which signals that the user is logged out of SP2 assert response.status_code == 302
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))