def setup_for_authn_req(self, idp_conf, sp_conf, nameid_format): base = self.construct_base_url_from_entity_id(idp_conf["entityid"]) config = {"idp_config": idp_conf, "endpoints": ENDPOINTS, "base": base, "state_id": "state_id"} sp_metadata_str = create_metadata_from_config_dict(sp_conf) idp_conf["metadata"]["inline"] = [sp_metadata_str] samlfrontend = SamlFrontend(lambda context, internal_req: (context, internal_req), INTERNAL_ATTRIBUTES, config) samlfrontend.register_endpoints(["saml"]) 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)) context = Context() context.state = State() context.request = parse.parse_qs( urlparse(fakesp.make_auth_req(samlfrontend.config["entityid"], nameid_format)).query) tmp_dict = {} for val in context.request: if isinstance(context.request[val], list): tmp_dict[val] = context.request[val][0] else: tmp_dict[val] = context.request[val] context.request = tmp_dict return context, samlfrontend
def test_start_auth_disco(self, sp_conf, idp_conf): """ Performs a complete test for the module satosa.backends.saml2. The flow should be accepted. """ samlbackend = SamlBackend(lambda context, internal_resp: (context, internal_resp), INTERNAL_ATTRIBUTES, {"config": sp_conf, "disco_srv": "https://my.dicso.com/role/idp.ds", "state_id": "saml_backend_test_id"}) test_state_key = "test_state_key_456afgrh" response_binding = BINDING_HTTP_REDIRECT fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf, metadata_construction=False)) internal_req = InternalRequest(UserIdHashType.persistent, "example.se/sp.xml") state = State() state.add(test_state_key, "my_state") context = Context() context.state = state resp = samlbackend.start_auth(context, internal_req) assert resp.status == "303 See Other", "Must be a redirect to the discovery server." disco_resp = parse_qs(urlparse(resp.message).query) info = parse_qs(urlparse(disco_resp["return"][0]).query) info[samlbackend.idp_disco_query_param] = idp_conf["entityid"] context = Context() context.request = info context.state = state resp = samlbackend.disco_response(context) assert resp.status == "303 See Other" req_params = dict(parse_qsl(urlparse(resp.message).query)) url, fake_idp_resp = fakeidp.handle_auth_req( req_params["SAMLRequest"], req_params["RelayState"], BINDING_HTTP_REDIRECT, "testuser1", response_binding=response_binding) context = Context() context.request = fake_idp_resp context.state = state context, internal_resp = samlbackend.authn_response(context, response_binding) assert isinstance(context, Context), "Not correct instance!" assert context.state.get(test_state_key) == "my_state", "Not correct state!" assert internal_resp.auth_info.auth_class_ref == PASSWORD, "Not correct authentication!" _dict = internal_resp.get_attributes() expected_data = {'surname': ['Testsson 1'], 'mail': ['*****@*****.**'], 'displayname': ['Test Testsson'], 'givenname': ['Test 1'], 'edupersontargetedid': ['one!for!all']} for key in _dict: assert expected_data[key] == _dict[key]
def test_with_pyoidc(self): responses.add(responses.POST, "https://graph.facebook.com/v2.5/oauth/access_token", body=json.dumps({"access_token": "qwerty", "token_type": "bearer", "expires_in": 9999999999999}), adding_headers={"set-cookie": "TEST=testing; path=/"}, status=200, content_type='application/json') responses.add(responses.GET, "https://graph.facebook.com/v2.5/me", match_querystring=False, body=json.dumps(FB_RESPONSE), status=200, content_type='application/json') context = Context() context.path = 'facebook/sso/redirect' context.state = State() internal_request = InternalRequest(UserIdHashType.transient, 'http://localhost:8087/sp.xml') get_state = Mock() get_state.return_value = STATE resp = self.fb_backend.start_auth(context, internal_request, get_state) context.cookie = resp.headers[0][1] context.request = { "code": FB_RESPONSE_CODE, "state": STATE } self.fb_backend.auth_callback_func = self.verify_callback self.fb_backend.authn_response(context)
def test_with_pyoidc(self): responses.add(responses.POST, "https://graph.facebook.com/v2.5/oauth/access_token", body=json.dumps({ "access_token": "qwerty", "token_type": "bearer", "expires_in": 9999999999999 }), adding_headers={"set-cookie": "TEST=testing; path=/"}, status=200, content_type='application/json') responses.add(responses.GET, "https://graph.facebook.com/v2.5/me", match_querystring=False, body=json.dumps(FB_RESPONSE), status=200, content_type='application/json') context = Context() context.path = 'facebook/sso/redirect' context.state = State() internal_request = InternalRequest(UserIdHashType.transient, 'http://localhost:8087/sp.xml') get_state = Mock() get_state.return_value = STATE resp = self.fb_backend.start_auth(context, internal_request, get_state) context.cookie = resp.headers[0][1] context.request = {"code": FB_RESPONSE_CODE, "state": STATE} self.fb_backend.auth_callback_func = self.verify_callback self.fb_backend.authn_response(context)
def run_server(self, environ, start_response, debug=False): path = environ.get('PATH_INFO', '').lstrip('/') if ".." in path: resp = Unauthorized() return resp(environ, start_response) context = Context() context.path = path # copy wsgi.input stream to allow it to be re-read later by satosa plugins # see: http://stackoverflow.com/questions/1783383/how-do-i-copy-wsgi-input-if-i-want-to-process-post-data-more-than-once content_length = int(environ.get('CONTENT_LENGTH', '0') or '0') body = io.BytesIO(environ['wsgi.input'].read(content_length)) environ['wsgi.input'] = body context.request = unpack_either(environ) environ['wsgi.input'].seek(0) context.wsgi_environ = environ context.cookie = environ.get("HTTP_COOKIE", "") try: resp = self.run(context) if isinstance(resp, Exception): raise resp return resp(environ, start_response) except SATOSANoBoundEndpointError: resp = NotFound("Couldn't find the side you asked for!") return resp(environ, start_response) except Exception as err: logger.exception("%s" % err) if debug: raise resp = ServiceError("%s" % err) return resp(environ, start_response)
def test_full_flow(self, context, idp_conf, sp_conf): test_state_key = "test_state_key_456afgrh" response_binding = BINDING_HTTP_REDIRECT fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf, metadata_construction=False)) context.state[test_state_key] = "my_state" # start auth flow (redirecting to discovery server) resp = self.samlbackend.start_auth(context, InternalRequest(None, None)) self.assert_redirect_to_discovery_server(resp, sp_conf) # fake response from discovery server disco_resp = parse_qs(urlparse(resp.message).query) info = parse_qs(urlparse(disco_resp["return"][0]).query) info["entityID"] = idp_conf["entityid"] request_context = Context() request_context.request = info request_context.state = context.state # pass discovery response to backend and check that it redirects to the selected IdP resp = self.samlbackend.disco_response(request_context) self.assert_redirect_to_idp(resp, idp_conf) # fake auth response to the auth request req_params = dict(parse_qsl(urlparse(resp.message).query)) url, fake_idp_resp = fakeidp.handle_auth_req( req_params["SAMLRequest"], req_params["RelayState"], BINDING_HTTP_REDIRECT, "testuser1", response_binding=response_binding) response_context = Context() response_context.request = fake_idp_resp response_context.state = request_context.state # pass auth response to backend and verify behavior self.samlbackend.authn_response(response_context, response_binding) context, internal_resp = self.samlbackend.auth_callback_func.call_args[ 0] assert self.samlbackend.name not in context.state assert context.state[test_state_key] == "my_state" self.assert_authn_response(internal_resp)
def test_full_flow(self, context, idp_conf, sp_conf): test_state_key = "test_state_key_456afgrh" response_binding = BINDING_HTTP_REDIRECT fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf, metadata_construction=False)) context.state[test_state_key] = "my_state" # start auth flow (redirecting to discovery server) resp = self.samlbackend.start_auth(context, InternalData()) self.assert_redirect_to_discovery_server(resp, sp_conf) # fake response from discovery server disco_resp = parse_qs(urlparse(resp.message).query) info = parse_qs(urlparse(disco_resp["return"][0]).query) info["entityID"] = idp_conf["entityid"] request_context = Context() request_context.request = info request_context.state = context.state # pass discovery response to backend and check that it redirects to the selected IdP resp = self.samlbackend.disco_response(request_context) self.assert_redirect_to_idp(resp, idp_conf) # fake auth response to the auth request req_params = dict(parse_qsl(urlparse(resp.message).query)) url, fake_idp_resp = fakeidp.handle_auth_req( req_params["SAMLRequest"], req_params["RelayState"], BINDING_HTTP_REDIRECT, "testuser1", response_binding=response_binding) response_context = Context() response_context.request = fake_idp_resp response_context.state = request_context.state # pass auth response to backend and verify behavior self.samlbackend.authn_response(response_context, response_binding) context, internal_resp = self.samlbackend.auth_callback_func.call_args[0] assert self.samlbackend.name not in context.state assert context.state[test_state_key] == "my_state" self.assert_authn_response(internal_resp)
def setup_authentication_response(self, state=None): context = Context() context.path = 'openid/authz_cb' op_base = TestConfiguration.get_instance().rp_config.OP_URL if not state: state = rndstr() context.request = { 'code': 'F+R4uWbN46U+Bq9moQPC4lEvRd2De4o=', 'scope': 'openid profile email address phone', 'state': state} context.state = self.generate_state(op_base) return context
def test_register_client_with_wrong_response_type(self): redirect_uri = "https://client.example.com" registration_request = RegistrationRequest(redirect_uris=[redirect_uri], response_types=["code"]) context = Context() context.request = registration_request.to_dict() registration_response = self.instance._register_client(context) assert registration_response.status == "400 Bad Request" error_response = ClientRegistrationErrorResponse().deserialize( registration_response.message, "json") assert error_response["error"] == "invalid_request" assert "response_type" in error_response["error_description"]
def setup_authentication_response(self, state=None): context = Context() context.path = 'openid/authz_cb' op_base = TestConfiguration.get_instance().rp_config.OP_URL if not state: state = rndstr() context.request = { 'code': 'F+R4uWbN46U+Bq9moQPC4lEvRd2De4o=', 'scope': 'openid profile email address phone', 'state': state } context.state = self.generate_state(op_base) return context
def setup_for_authn_req(self, idp_conf, sp_conf, nameid_format): base = self.construct_base_url_from_entity_id(idp_conf["entityid"]) config = { "idp_config": idp_conf, "endpoints": ENDPOINTS, "base": base, "state_id": "state_id" } sp_metadata_str = create_metadata_from_config_dict(sp_conf) idp_conf["metadata"]["inline"] = [sp_metadata_str] samlfrontend = SamlFrontend( lambda context, internal_req: (context, internal_req), INTERNAL_ATTRIBUTES, config) samlfrontend.register_endpoints(["saml"]) 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)) context = Context() context.state = State() context.request = parse.parse_qs( urlparse( fakesp.make_auth_req(samlfrontend.config["entityid"], nameid_format)).query) tmp_dict = {} for val in context.request: if isinstance(context.request[val], list): tmp_dict[val] = context.request[val][0] else: tmp_dict[val] = context.request[val] context.request = tmp_dict return context, samlfrontend
def test_register_client(self): redirect_uri = "https://client.example.com" registration_request = RegistrationRequest(redirect_uris=[redirect_uri], response_types=["id_token"]) context = Context() context.request = registration_request.to_dict() registration_response = self.instance._register_client(context) assert registration_response.status == "201 Created" reg_resp = RegistrationResponse().deserialize(registration_response.message, "json") assert "client_id" in reg_resp assert reg_resp["client_id"] in self.instance.provider.cdb # no need to issue client secret since to token endpoint is published assert "client_secret" not in reg_resp assert reg_resp["redirect_uris"] == [redirect_uri] assert reg_resp["response_types"] == ["id_token"] assert reg_resp["id_token_signed_response_alg"] == "RS256"
def test_authn_response(self): context = Context() context.path = 'facebook/sso/redirect' context.state = State() internal_request = InternalRequest(UserIdHashType.transient, 'http://localhost:8087/sp.xml') get_state = Mock() get_state.return_value = STATE resp = self.fb_backend.start_auth(context, internal_request, get_state) context.cookie = resp.headers[0][1] context.request = {"code": FB_RESPONSE_CODE, "state": STATE} # context.request = json.dumps(context.request) self.fb_backend.auth_callback_func = self.verify_callback tmp_consumer = self.fb_backend.get_consumer() tmp_consumer.do_access_token_request = self.verify_do_access_token_request self.fb_backend.get_consumer = Mock() self.fb_backend.get_consumer.return_value = tmp_consumer self.fb_backend.request_fb = self.verify_request_fb self.fb_backend.authn_response(context)
def test_authn_response(self): context = Context() context.path = 'facebook/sso/redirect' context.state = State() internal_request = InternalRequest(UserIdHashType.transient, 'http://localhost:8087/sp.xml') get_state = Mock() get_state.return_value = STATE resp = self.fb_backend.start_auth(context, internal_request, get_state) context.cookie = resp.headers[0][1] context.request = { "code": FB_RESPONSE_CODE, "state": STATE } # context.request = json.dumps(context.request) self.fb_backend.auth_callback_func = self.verify_callback tmp_consumer = self.fb_backend.get_consumer() tmp_consumer.do_access_token_request = self.verify_do_access_token_request self.fb_backend.get_consumer = Mock() self.fb_backend.get_consumer.return_value = tmp_consumer self.fb_backend.request_fb = self.verify_request_fb self.fb_backend.authn_response(context)
def run_server(self, environ, start_response): path = environ.get("PATH_INFO", "").lstrip("/") if ".." in path: resp = Unauthorized() return resp(environ, start_response) context = Context() context.path = path context.request = unpack_either(environ) context.cookie = environ.get("HTTP_COOKIE", "") try: resp = self.run(context) if isinstance(resp, Exception): raise resp return resp(environ, start_response) except SATOSANoBoundEndpointError: resp = NotFound("Couldn't find the side you asked for!") return resp(environ, start_response) except Exception as err: logger.exception("%s" % err) resp = ServiceError("%s" % err) return resp(environ, start_response)
def test_explicity_type_in_scope(self, id_type, expected_backend): context = Context() context.request = {'scope': 'openid student ' + id_type} self.service.process(context, None) assert context.target_backend == expected_backend
def test_defaults_to_transient(self): context = Context() context.request = {'scope': 'openid student'} self.service.process(context, None) assert context.target_backend == 'SAML2Transient'
def test_explicity_type_in_scope(self, id_type, expected_backend): context = Context() context.request = {'scope': 'openid student ' + id_type} self.service.process(context, None) assert context.target_backend == expected_backend
def test_defaults_to_transient(self): context = Context() context.request = {'scope': 'openid student'} self.service.process(context, None) assert context.target_backend == 'SAML2Transient'
def test_start_auth_disco(self, sp_conf, idp_conf): """ Performs a complete test for the module satosa.backends.saml2. The flow should be accepted. """ samlbackend = SamlBackend( lambda context, internal_resp: (context, internal_resp), INTERNAL_ATTRIBUTES, { "config": sp_conf, "disco_srv": "https://my.dicso.com/role/idp.ds", "state_id": "saml_backend_test_id" }) test_state_key = "test_state_key_456afgrh" response_binding = BINDING_HTTP_REDIRECT fakeidp = FakeIdP(USERS, config=IdPConfig().load(idp_conf, metadata_construction=False)) internal_req = InternalRequest(UserIdHashType.persistent, "example.se/sp.xml") state = State() state.add(test_state_key, "my_state") context = Context() context.state = state resp = samlbackend.start_auth(context, internal_req) assert resp.status == "303 See Other", "Must be a redirect to the discovery server." disco_resp = parse_qs(urlparse(resp.message).query) info = parse_qs(urlparse(disco_resp["return"][0]).query) info[samlbackend.idp_disco_query_param] = idp_conf["entityid"] context = Context() context.request = info context.state = state resp = samlbackend.disco_response(context) assert resp.status == "303 See Other" req_params = dict(parse_qsl(urlparse(resp.message).query)) url, fake_idp_resp = fakeidp.handle_auth_req( req_params["SAMLRequest"], req_params["RelayState"], BINDING_HTTP_REDIRECT, "testuser1", response_binding=response_binding) context = Context() context.request = fake_idp_resp context.state = state context, internal_resp = samlbackend.authn_response( context, response_binding) assert isinstance(context, Context), "Not correct instance!" assert context.state.get( test_state_key) == "my_state", "Not correct state!" assert internal_resp.auth_info.auth_class_ref == PASSWORD, "Not correct authentication!" _dict = internal_resp.get_attributes() expected_data = { 'surname': ['Testsson 1'], 'mail': ['*****@*****.**'], 'displayname': ['Test Testsson'], 'givenname': ['Test 1'], 'edupersontargetedid': ['one!for!all'] } for key in _dict: assert expected_data[key] == _dict[key]