def get_auth_response(self, samlfrontend, context, internal_response, sp_conf, idp_metadata_str): sp_config = SPConfig().load(sp_conf, metadata_construction=False) resp_args = { "name_id_policy": NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT), "in_response_to": None, "destination": sp_config.endpoint("assertion_consumer_service", binding=BINDING_HTTP_REDIRECT)[0], "sp_entity_id": sp_conf["entityid"], "binding": BINDING_HTTP_REDIRECT } request_state = samlfrontend._create_state_data(context, resp_args, "") context.state[samlfrontend.name] = request_state resp = samlfrontend.handle_authn_response(context, internal_response) sp_conf["metadata"]["inline"].append(idp_metadata_str) fakesp = FakeSP(sp_config) resp_dict = parse_qs(urlparse(resp.message).query) return fakesp.parse_authn_request_response( resp_dict["SAMLResponse"][0], BINDING_HTTP_REDIRECT)
def test_handle_authn_request_without_name_id_policy( self, idp_conf, sp_conf): """ Performs a complete test for the module. The flow should be accepted. """ context, samlfrontend = self.setup_for_authn_req(idp_conf, sp_conf, "") _, internal_req = samlfrontend.handle_authn_request( context, BINDING_HTTP_REDIRECT) assert internal_req.requestor == sp_conf["entityid"] auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", "unittest_idp.xml") internal_response = InternalResponse(auth_info=auth_info) internal_response.set_user_id_hash_type(internal_req.user_id_hash_type) internal_response.add_attributes(USERS["testuser1"]) resp = samlfrontend.handle_authn_response(context, internal_response) resp_dict = parse_qs(urlparse(resp.message).query) fakesp = FakeSP(None, config=SPConfig().load(sp_conf, metadata_construction=False)) resp = fakesp.parse_authn_request_response( resp_dict['SAMLResponse'][0], BINDING_HTTP_REDIRECT) for key in resp.ava: assert USERS["testuser1"][key] == resp.ava[key]
def test_handle_authn_request(self, context, idp_conf, sp_conf, internal_response): samlfrontend = self.setup_for_authn_req(context, idp_conf, sp_conf) _, internal_req = samlfrontend.handle_authn_request(context, BINDING_HTTP_REDIRECT) assert internal_req.requester == sp_conf["entityid"] resp = samlfrontend.handle_authn_response(context, internal_response) resp_dict = parse_qs(urlparse(resp.message).query) fakesp = FakeSP(SPConfig().load(sp_conf, metadata_construction=False)) resp = fakesp.parse_authn_request_response(resp_dict["SAMLResponse"][0], BINDING_HTTP_REDIRECT) for key in resp.ava: assert USERS["testuser1"][key] == resp.ava[key]
def test_acr_mapping_per_idp_in_authn_response(self, idp_conf, sp_conf): expected_loa = "LoA1" loa = { "": "http://eidas.europa.eu/LoA/low", idp_conf["entityid"]: expected_loa } base = self.construct_base_url_from_entity_id(idp_conf["entityid"]) conf = { "idp_config": idp_conf, "endpoints": ENDPOINTS, "base": base, "state_id": "state_id", "acr_mapping": loa } samlfrontend = SamlFrontend(None, INTERNAL_ATTRIBUTES, conf) samlfrontend.register_endpoints(["foo"]) idp_metadata_str = create_metadata_from_config_dict( samlfrontend.config) sp_conf["metadata"]["inline"].append(idp_metadata_str) fakesp = FakeSP(None, config=SPConfig().load(sp_conf, metadata_construction=False)) auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", idp_conf["entityid"]) internal_response = InternalResponse(auth_info=auth_info) context = Context() context.state = State() resp_args = { "name_id_policy": NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT), "in_response_to": None, "destination": "", "sp_entity_id": None, "binding": BINDING_HTTP_REDIRECT } request_state = samlfrontend.save_state(context, resp_args, "") context.state.add(conf["state_id"], request_state) resp = samlfrontend.handle_authn_response(context, internal_response) resp_dict = parse_qs(urlparse(resp.message).query) resp = fakesp.parse_authn_request_response( resp_dict['SAMLResponse'][0], BINDING_HTTP_REDIRECT) assert len(resp.assertion.authn_statement) == 1 authn_context_class_ref = resp.assertion.authn_statement[ 0].authn_context.authn_context_class_ref assert authn_context_class_ref.text == expected_loa
def run_test(self, satosa_config_dict, sp_conf, oidc_backend_config, frontend_config): subject_id = "testuser1" # proxy config satosa_config_dict["FRONTEND_MODULES"] = [frontend_config] satosa_config_dict["BACKEND_MODULES"] = [oidc_backend_config] satosa_config_dict["INTERNAL_ATTRIBUTES"]["attributes"] = {attr_name: {"openid": [attr_name], "saml": [attr_name]} for attr_name in USERS[subject_id]} frontend_metadata, backend_metadata = create_entity_descriptors(SATOSAConfig(satosa_config_dict)) # application test_client = Client(make_app(SATOSAConfig(satosa_config_dict)), BaseResponse) # config test SP frontend_metadata_str = str(frontend_metadata[frontend_config["name"]][0]) sp_conf["metadata"]["inline"].append(frontend_metadata_str) fakesp = FakeSP(SPConfig().load(sp_conf, metadata_construction=False)) # create auth req destination, req_args = fakesp.make_auth_req(frontend_metadata[frontend_config["name"]][0].entity_id) auth_req = urlparse(destination).path + "?" + urlencode(req_args) # make auth req to proxy proxied_auth_req = test_client.get(auth_req) assert proxied_auth_req.status == "302 Found" parsed_auth_req = dict(parse_qsl(urlparse(proxied_auth_req.data.decode("utf-8")).query)) # create auth resp id_token_claims = {k: v[0] for k, v in USERS[subject_id].items()} id_token_claims["sub"] = subject_id id_token_claims["iat"] = time.time() id_token_claims["exp"] = time.time() + 3600 id_token_claims["iss"] = "https://op.example.com" id_token_claims["aud"] = oidc_backend_config["config"]["client"]["client_metadata"]["client_id"] id_token_claims["nonce"] = parsed_auth_req["nonce"] id_token = IdToken(**id_token_claims).to_jwt() authn_resp = {"state": parsed_auth_req["state"], "id_token": id_token} # make auth resp to proxy redirect_uri_path = urlparse( oidc_backend_config["config"]["client"]["client_metadata"]["redirect_uris"][0]).path authn_resp_req = redirect_uri_path + "?" + urlencode(authn_resp) authn_resp = test_client.get(authn_resp_req) assert authn_resp.status == "303 See Other" # verify auth resp from proxy resp_dict = dict(parse_qsl(urlparse(authn_resp.data.decode("utf-8")).query)) auth_resp = fakesp.parse_authn_request_response(resp_dict["SAMLResponse"], BINDING_HTTP_REDIRECT) assert auth_resp.ava == USERS[subject_id]
def run_test(self, satosa_config_dict, sp_conf, idp_conf, saml_backend_config, frontend_config): user_id = "testuser1" # proxy config satosa_config_dict["FRONTEND_MODULES"] = [frontend_config] satosa_config_dict["BACKEND_MODULES"] = [saml_backend_config] satosa_config_dict["INTERNAL_ATTRIBUTES"]["attributes"] = {attr_name: {"saml": [attr_name]} for attr_name in USERS[user_id]} frontend_metadata, backend_metadata = create_entity_descriptors(SATOSAConfig(satosa_config_dict)) # application test_client = Client(make_app(SATOSAConfig(satosa_config_dict)), BaseResponse) # config test SP frontend_metadata_str = str(frontend_metadata[frontend_config["name"]][0]) sp_conf["metadata"]["inline"].append(frontend_metadata_str) fakesp = FakeSP(SPConfig().load(sp_conf, metadata_construction=False)) # create auth req destination, req_args = fakesp.make_auth_req(frontend_metadata[frontend_config["name"]][0].entity_id) auth_req = urlparse(destination).path + "?" + urlencode(req_args) # make auth req to proxy proxied_auth_req = test_client.get(auth_req) assert proxied_auth_req.status == "303 See Other" # config test IdP backend_metadata_str = str(backend_metadata[saml_backend_config["name"]][0]) idp_conf["metadata"]["inline"].append(backend_metadata_str) fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf, metadata_construction=False)) # create auth resp req_params = dict(parse_qsl(urlparse(proxied_auth_req.data.decode("utf-8")).query)) url, authn_resp = fakeidp.handle_auth_req( req_params["SAMLRequest"], req_params["RelayState"], BINDING_HTTP_REDIRECT, user_id, response_binding=BINDING_HTTP_REDIRECT) # make auth resp to proxy authn_resp_req = urlparse(url).path + "?" + urlencode(authn_resp) authn_resp = test_client.get("/" + authn_resp_req) assert authn_resp.status == "303 See Other" # verify auth resp from proxy resp_dict = dict(parse_qsl(urlparse(authn_resp.data.decode("utf-8")).query)) auth_resp = fakesp.parse_authn_request_response(resp_dict["SAMLResponse"], BINDING_HTTP_REDIRECT) assert auth_resp.ava == USERS[user_id]
def get_auth_response(self, samlfrontend, context, internal_response, sp_conf, idp_metadata_str): sp_config = SPConfig().load(sp_conf, metadata_construction=False) resp_args = { "name_id_policy": NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT), "in_response_to": None, "destination": sp_config.endpoint("assertion_consumer_service", binding=BINDING_HTTP_REDIRECT)[0], "sp_entity_id": sp_conf["entityid"], "binding": BINDING_HTTP_REDIRECT } request_state = samlfrontend._create_state_data(context, resp_args, "") context.state[samlfrontend.name] = request_state resp = samlfrontend.handle_authn_response(context, internal_response) sp_conf["metadata"]["inline"].append(idp_metadata_str) fakesp = FakeSP(sp_config) resp_dict = parse_qs(urlparse(resp.message).query) return fakesp.parse_authn_request_response(resp_dict["SAMLResponse"][0], BINDING_HTTP_REDIRECT)
def test_handle_authn_response_without_name_id( self, context, idp_conf, sp_conf, internal_response): samlfrontend = self.setup_for_authn_req( context, idp_conf, sp_conf, relay_state=None) _, internal_req = samlfrontend.handle_authn_request( context, BINDING_HTTP_REDIRECT) # Make sure we are testing the equivalent of a <Response> with no # <NameID> in the <Subject>. assert internal_response.subject_type is None assert internal_response.subject_id is None resp = samlfrontend.handle_authn_response(context, internal_response) resp_dict = parse_qs(urlparse(resp.message).query) fakesp = FakeSP(SPConfig().load(sp_conf, metadata_construction=False)) resp = fakesp.parse_authn_request_response( resp_dict["SAMLResponse"][0], BINDING_HTTP_REDIRECT) # The <NameID> must not have an empty TextContent. assert resp.name_id.text is not None
def test_handle_authn_request_without_name_id_policy(self, idp_conf, sp_conf): """ Performs a complete test for the module. The flow should be accepted. """ context, samlfrontend = self.setup_for_authn_req(idp_conf, sp_conf, "") _, internal_req = samlfrontend.handle_authn_request(context, BINDING_HTTP_REDIRECT) assert internal_req.requestor == sp_conf["entityid"] auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", "unittest_idp.xml") internal_response = InternalResponse(auth_info=auth_info) internal_response.set_user_id_hash_type(internal_req.user_id_hash_type) internal_response.add_attributes(USERS["testuser1"]) resp = samlfrontend.handle_authn_response(context, internal_response) resp_dict = parse_qs(urlparse(resp.message).query) fakesp = FakeSP(None, config=SPConfig().load(sp_conf, metadata_construction=False)) resp = fakesp.parse_authn_request_response(resp_dict['SAMLResponse'][0], BINDING_HTTP_REDIRECT) for key in resp.ava: assert USERS["testuser1"][key] == resp.ava[key]
def test_acr_mapping_per_idp_in_authn_response(self, idp_conf, sp_conf): expected_loa = "LoA1" loa = {"": "http://eidas.europa.eu/LoA/low", idp_conf["entityid"]: expected_loa} base = self.construct_base_url_from_entity_id(idp_conf["entityid"]) conf = {"idp_config": idp_conf, "endpoints": ENDPOINTS, "base": base, "state_id": "state_id", "acr_mapping": loa} samlfrontend = SamlFrontend(None, INTERNAL_ATTRIBUTES, conf) samlfrontend.register_endpoints(["foo"]) idp_metadata_str = create_metadata_from_config_dict(samlfrontend.config) sp_conf["metadata"]["inline"].append(idp_metadata_str) fakesp = FakeSP(None, config=SPConfig().load(sp_conf, metadata_construction=False)) auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", idp_conf["entityid"]) internal_response = InternalResponse(auth_info=auth_info) context = Context() context.state = State() resp_args = { "name_id_policy": NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT), "in_response_to": None, "destination": "", "sp_entity_id": None, "binding": BINDING_HTTP_REDIRECT } request_state = samlfrontend.save_state(context, resp_args, "") context.state.add(conf["state_id"], request_state) resp = samlfrontend.handle_authn_response(context, internal_response) resp_dict = parse_qs(urlparse(resp.message).query) resp = fakesp.parse_authn_request_response(resp_dict['SAMLResponse'][0], BINDING_HTTP_REDIRECT) assert len(resp.assertion.authn_statement) == 1 authn_context_class_ref = resp.assertion.authn_statement[ 0].authn_context.authn_context_class_ref assert authn_context_class_ref.text == expected_loa
def test_respect_sp_entity_categories(self, entity_category, expected_attributes, idp_conf, sp_conf): base = self.construct_base_url_from_entity_id(idp_conf["entityid"]) conf = {"idp_config": idp_conf, "endpoints": ENDPOINTS, "base": base, "state_id": "state_id"} internal_attributes = {attr_name: {"saml": [attr_name.lower()]} for attr_name in expected_attributes} samlfrontend = SamlFrontend(None, dict(attributes=internal_attributes), conf) samlfrontend.register_endpoints(["foo"]) idp_metadata_str = create_metadata_from_config_dict(samlfrontend.idp_config) sp_conf["metadata"]["inline"].append(idp_metadata_str) sp_conf["entity_category"] = entity_category fakesp = FakeSP(None, config=SPConfig().load(sp_conf, metadata_construction=False)) auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", idp_conf["entityid"]) internal_response = InternalResponse(auth_info=auth_info) user_attributes = {k: "foo" for k in expected_attributes} user_attributes.update({k: "bar" for k in ["extra", "more", "stuff"]}) internal_response.add_attributes(user_attributes) context = Context() context.state = State() resp_args = { "name_id_policy": NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT), "in_response_to": None, "destination": "", "sp_entity_id": None, "binding": BINDING_HTTP_REDIRECT } request_state = samlfrontend.save_state(context, resp_args, "") context.state.add(conf["state_id"], request_state) resp = samlfrontend.handle_authn_response(context, internal_response) resp_dict = parse_qs(urlparse(resp.message).query) resp = fakesp.parse_authn_request_response(resp_dict['SAMLResponse'][0], BINDING_HTTP_REDIRECT) assert Counter(resp.ava.keys()) == Counter(expected_attributes)
def test_handle_authn_response_without_relay_state(self, context, idp_conf, sp_conf, internal_response): samlfrontend = self.setup_for_authn_req(context, idp_conf, sp_conf, relay_state=None) _, internal_req = samlfrontend.handle_authn_request( context, BINDING_HTTP_REDIRECT) assert internal_req.requester == sp_conf["entityid"] resp = samlfrontend.handle_authn_response(context, internal_response) resp_dict = parse_qs(urlparse(resp.message).query) fakesp = FakeSP(SPConfig().load(sp_conf)) resp = fakesp.parse_authn_request_response( resp_dict["SAMLResponse"][0], BINDING_HTTP_REDIRECT) for key in resp.ava: assert USERS["testuser1"][key] == resp.ava[key] assert samlfrontend.name not in context.state
class TestProxy: """ Performs a complete flow test for the proxy. Verifies SAML -> PROXY -> SAML. """ @pytest.fixture(autouse=True) def setup(self): """ Initiates the test. :return: None """ self.sp = FakeSP(None, config=SPConfig().load( TestConfiguration.get_instance().fake_sp_config, metadata_construction=False)) self.idp = FakeIdP( USERS, IdPConfig().load(TestConfiguration.get_instance().fake_idp_config, metadata_construction=False)) def test_flow(self): """ Performs the test. """ e_id = 'https://localhost:8090/proxy.xml' target_id = 'https://example.com/unittest_idp.xml' url = "{}&entityID={}".format(self.sp.make_auth_req(e_id), quote(target_id)) app = WsgiApplication( config=TestConfiguration.get_instance().proxy_config) test_client = Client(app.run_server, BaseResponse) parsed = urlparse(url) request = "{}?{}".format(parsed.path, parsed.query) resp = test_client.get(request) assert resp.status == '303 See Other' headers = dict(resp.headers) assert headers["Set-Cookie"], "Did not save state in cookie!" url = headers["location"] req = parse_qs(urlsplit(url).query) assert 'SAMLRequest' in req assert 'RelayState' in req action, body = self.idp.handle_auth_req(req['SAMLRequest'][0], req['RelayState'][0], BINDING_HTTP_REDIRECT, 'testuser1') parsed = urlparse(action) request = "{}?{}".format(parsed.path, parsed.query) resp = test_client.post(request, data=urlencode(body), headers=[("Cookie", headers["Set-Cookie"]), ("Content-Type", "application/x-www-form-urlencoded") ]) assert resp.status == '302 Found' headers = dict(resp.headers) url = headers["location"] req = parse_qs(urlsplit(url).query) assert 'SAMLResponse' in req assert 'RelayState' in req resp = self.sp.parse_authn_request_response(req['SAMLResponse'][0], BINDING_HTTP_REDIRECT) identity = resp.ava assert identity["displayName"][0] == "Test Testsson"
class TestProxy: """ Performs a complete flow test for the proxy. Verifies SAML -> PROXY -> SAML. """ @pytest.fixture(autouse=True) def setup(self): """ Initiates the test. :return: None """ self.sp = FakeSP(None, config=SPConfig().load(TestConfiguration.get_instance(). fake_sp_config, metadata_construction=False)) self.idp = FakeIdP(USERS, IdPConfig().load(TestConfiguration.get_instance().fake_idp_config, metadata_construction=False)) def test_flow(self): """ Performs the test. """ e_id = 'https://localhost:8090/proxy.xml' target_id = 'https://example.com/unittest_idp.xml' url = "{}&entityID={}".format(self.sp.make_auth_req(e_id), quote(target_id)) app = WsgiApplication(config=TestConfiguration.get_instance().proxy_config) test_client = Client(app.run_server, BaseResponse) parsed = urlparse(url) request = "{}?{}".format(parsed.path, parsed.query) resp = test_client.get(request) assert resp.status == '303 See Other' headers = dict(resp.headers) assert headers["Set-Cookie"], "Did not save state in cookie!" url = headers["location"] req = parse_qs(urlsplit(url).query) assert 'SAMLRequest' in req assert 'RelayState' in req action, body = self.idp.handle_auth_req(req['SAMLRequest'][0], req['RelayState'][0], BINDING_HTTP_REDIRECT, 'testuser1') parsed = urlparse(action) request = "{}?{}".format(parsed.path, parsed.query) resp = test_client.post(request, data=urlencode(body), headers=[("Cookie", headers["Set-Cookie"]), ("Content-Type", "application/x-www-form-urlencoded")]) assert resp.status == '302 Found' headers = dict(resp.headers) url = headers["location"] req = parse_qs(urlsplit(url).query) assert 'SAMLResponse' in req assert 'RelayState' in req resp = self.sp.parse_authn_request_response(req['SAMLResponse'][0], BINDING_HTTP_REDIRECT) identity = resp.ava assert identity["displayName"][0] == "Test Testsson"
def run_test(self, satosa_config_dict, sp_conf, idp_conf, saml_backend_config, frontend_config): subject_id = "testuser1" # proxy config satosa_config_dict["FRONTEND_MODULES"] = [frontend_config] satosa_config_dict["BACKEND_MODULES"] = [saml_backend_config] satosa_config_dict["INTERNAL_ATTRIBUTES"]["attributes"] = { attr_name: { "saml": [attr_name] } for attr_name in USERS[subject_id] } frontend_metadata, backend_metadata = create_entity_descriptors( SATOSAConfig(satosa_config_dict)) # application test_client = Client(make_app(SATOSAConfig(satosa_config_dict)), BaseResponse) # config test SP frontend_metadata_str = str( frontend_metadata[frontend_config["name"]][0]) sp_conf["metadata"]["inline"].append(frontend_metadata_str) fakesp = FakeSP(SPConfig().load(sp_conf, metadata_construction=False)) # create auth req destination, req_args = fakesp.make_auth_req( frontend_metadata[frontend_config["name"]][0].entity_id) auth_req = urlparse(destination).path + "?" + urlencode(req_args) # make auth req to proxy proxied_auth_req = test_client.get(auth_req) assert proxied_auth_req.status == "303 See Other" # config test IdP backend_metadata_str = str( backend_metadata[saml_backend_config["name"]][0]) idp_conf["metadata"]["inline"].append(backend_metadata_str) fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf, metadata_construction=False)) # create auth resp req_params = dict( parse_qsl(urlparse(proxied_auth_req.data.decode("utf-8")).query)) url, authn_resp = fakeidp.handle_auth_req( req_params["SAMLRequest"], req_params["RelayState"], BINDING_HTTP_REDIRECT, subject_id, response_binding=BINDING_HTTP_REDIRECT) # make auth resp to proxy authn_resp_req = urlparse(url).path + "?" + urlencode(authn_resp) authn_resp = test_client.get("/" + authn_resp_req) assert authn_resp.status == "303 See Other" # verify auth resp from proxy resp_dict = dict( parse_qsl(urlparse(authn_resp.data.decode("utf-8")).query)) auth_resp = fakesp.parse_authn_request_response( resp_dict["SAMLResponse"], BINDING_HTTP_REDIRECT) assert auth_resp.ava == USERS[subject_id]