Repository: IdentityPython/pysaml2

Issue:
Saml2Client.apply_binding() should return HTTP status code
saml2.client.Saml2Client.apply_binding() should return a item 'status' in the returned dict. The status should be the HTTP status code which should be used in the response.


Discussion:
peppelinux: It seems that only the asserts on http params length in unit tests must be fixed. CI unfortunately failed

Patch:
diff --git a/src/saml2/entity.py b/src/saml2/entity.py
index c9572aef..672ad6f7 100644
--- a/src/saml2/entity.py
+++ b/src/saml2/entity.py
@@ -241,7 +241,7 @@ class Entity(HTTPBase):
             if response:
                 info = self.use_http_artifact(msg_str, destination, relay_state)
                 info["method"] = "GET"
-                info["status"] = 302
+                info["status"] = 302  # TODO: should be 303 on >= HTTP/1.1
             else:
                 info = self.use_http_artifact(msg_str, destination, relay_state)
         else:
diff --git a/src/saml2/pack.py b/src/saml2/pack.py
index 090b13b4..f8fdbfcb 100644
--- a/src/saml2/pack.py
+++ b/src/saml2/pack.py
@@ -111,7 +111,7 @@ def http_form_post_message(message, location, relay_state="",
         relay_state_input=relay_state_input,
         action=location)
 
-    return {"headers": [("Content-type", "text/html")], "data": response}
+    return {"headers": [("Content-type", "text/html")], "data": response, "status": 200}
 
 
 def http_post_message(message, relay_state="", typ="SAMLRequest", **kwargs):
@@ -137,7 +137,8 @@ def http_post_message(message, relay_state="", typ="SAMLRequest", **kwargs):
         part["RelayState"] = relay_state
 
     return {"headers": [("Content-type", 'application/x-www-form-urlencoded')],
-            "data": urlencode(part)}
+            "data": urlencode(part),
+            "status": 200}
 
 
 def http_redirect_message(message, location, relay_state="", typ="SAMLRequest",
@@ -197,7 +198,7 @@ def http_redirect_message(message, location, relay_state="", typ="SAMLRequest",
     headers = [('Location', str(login_url))]
     body = []
 
-    return {"headers": headers, "data": body}
+    return {"headers": headers, "data": body, "status": 303}
 
 
 DUMMY_NAMESPACE = "http://example.org/"
@@ -257,12 +258,14 @@ def make_soap_enveloped_saml_thingy(thingy, header_parts=None):
 
 def http_soap_message(message):
     return {"headers": [("Content-type", "application/soap+xml")],
-            "data": make_soap_enveloped_saml_thingy(message)}
+            "data": make_soap_enveloped_saml_thingy(message),
+            "status": 200}
 
 
 def http_paos(message, extra=None):
     return {"headers": [("Content-type", "application/soap+xml")],
-            "data": make_soap_enveloped_saml_thingy(message, extra)}
+            "data": make_soap_enveloped_saml_thingy(message, extra),
+            "status": 200}
 
 
 def parse_soap_enveloped_saml(text, body_class, header_class=None):


Test patch:
diff --git a/tests/fakeIDP.py b/tests/fakeIDP.py
index 123c347a..c2bd5612 100644
--- a/tests/fakeIDP.py
+++ b/tests/fakeIDP.py
@@ -43,8 +43,8 @@ def unpack_form(_str, ver="SAMLRequest"):
 
 
 class DummyResponse(object):
-    def __init__(self, code, data, headers=None):
-        self.status_code = code
+    def __init__(self, status, data, headers=None):
+        self.status_code = status
         self.text = data
         self.headers = headers or []
         self.content = data
@@ -130,7 +130,7 @@ class FakeIDP(Server):
         _dict = pack.factory(_binding, response,
                              resp_args["destination"], relay_state,
                              "SAMLResponse")
-        return DummyResponse(200, **_dict)
+        return DummyResponse(**_dict)
 
     def attribute_query_endpoint(self, xml_str, binding):
         if binding == BINDING_SOAP:
@@ -160,7 +160,7 @@ class FakeIDP(Server):
         else:  # Just POST
             response = "%s" % attr_resp
 
-        return DummyResponse(200, response)
+        return DummyResponse(status=200, data=response)
 
     def logout_endpoint(self, xml_str, binding):
         if binding == BINDING_SOAP:
@@ -185,4 +185,4 @@ class FakeIDP(Server):
         else: # Just POST
             response = "%s" % _resp
 
-        return DummyResponse(200, response)
+        return DummyResponse(status=200, data=response)
diff --git a/tests/test_50_server.py b/tests/test_50_server.py
index 2aee6d8a..7ee82499 100644
--- a/tests/test_50_server.py
+++ b/tests/test_50_server.py
@@ -2311,9 +2311,10 @@ class TestServerLogout():
                 binding, "%s" % response, destination, "relay_state", response=True
             )
 
-            assert len(http_args) == 4
+            assert len(http_args) == 5
             assert http_args["headers"][0][0] == "Location"
             assert http_args["data"] == []
+            assert http_args["status"] == 303
             assert http_args['url'] == 'http://lingon.catalogix.se:8087/sloresp'
 
     def test_2(self):
@@ -2330,10 +2331,11 @@ class TestServerLogout():
                 binding, "%s" % response, destination, "relay_state", response=True
             )
 
-            assert len(http_args) == 4
+            assert len(http_args) == 5
             assert len(http_args["data"]) > 0
             assert http_args["method"] == "POST"
             assert http_args['url'] == 'http://lingon.catalogix.se:8087/slo'
+            assert http_args['status'] == 200
 
 
 if __name__ == "__main__":
diff --git a/tests/test_51_client.py b/tests/test_51_client.py
index c1d52531..d30a8746 100644
--- a/tests/test_51_client.py
+++ b/tests/test_51_client.py
@@ -3167,9 +3167,10 @@ class TestClientWithDummy():
             binding=binding, response_binding=response_binding)
 
         assert isinstance(sid, six.string_types)
-        assert len(http_args) == 4
+        assert len(http_args) == 5
         assert http_args["headers"][0][0] == "Location"
         assert http_args["data"] == []
+        assert http_args["status"] == 303
         redirect_url = http_args["headers"][0][1]
         _, _, _, _, qs, _ = parse.urlparse(redirect_url)
         qs_dict = parse.parse_qs(qs)
@@ -3188,9 +3189,10 @@ class TestClientWithDummy():
 
         assert binding == auth_binding
         assert isinstance(sid, six.string_types)
-        assert len(http_args) == 4
+        assert len(http_args) == 5
         assert http_args["headers"][0][0] == "Location"
         assert http_args["data"] == []
+        assert http_args["status"] == 303
         redirect_url = http_args["headers"][0][1]
         _, _, _, _, qs, _ = parse.urlparse(redirect_url)
         qs_dict = parse.parse_qs(qs)