From 6b50545babea83ba279bea642cd475953e7cdf11 Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 10:40:47 +0200
Subject: [PATCH 1/9] migrate test delegation

---
 .../tests/functional/test_delegation.py       | 269 ++++++++++++++++++
 1 file changed, 269 insertions(+)
 create mode 100644 src/fts3rest/fts3rest/tests/functional/test_delegation.py

diff --git a/src/fts3rest/fts3rest/tests/functional/test_delegation.py b/src/fts3rest/fts3rest/tests/functional/test_delegation.py
new file mode 100644
index 00000000..cd771a04
--- /dev/null
+++ b/src/fts3rest/fts3rest/tests/functional/test_delegation.py
@@ -0,0 +1,269 @@
+from datetime import datetime, timedelta
+from M2Crypto import EVP
+import time
+
+from fts3rest.controllers.delegation import _generate_proxy_request
+from fts3rest.tests import TestController
+from fts3rest.model.meta import Session
+from fts3.model import Credential, CredentialCache
+
+
+class TestDelegation(TestController):
+    """
+    Tests for the delegation controller
+    """
+
+    def test_get_termination_time_not_existing(self):
+        """
+        Get the termination time for a dlg_id that hasn't delegated yet
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        delegation_id = self.app.get(
+            url="/delegation/%s" % creds.delegation_id, status=200
+        ).json
+        self.assertEqual(delegation_id, None)
+
+    def test_get_termination_time(self):
+        """
+        Get credentials termination time
+        """
+        self.test_valid_proxy()
+        creds = self.get_user_credentials()
+
+        termination_str = self.app.get(
+            url="/delegation/%s" % creds.delegation_id, status=200
+        ).json["termination_time"]
+
+        termination = datetime.strptime(termination_str, "%Y-%m-%dT%H:%M:%S")
+        self.assertGreater(
+            termination, datetime.utcnow() + timedelta(hours=2, minutes=58)
+        )
+
+    def test_put_cred_without_cache(self):
+        """
+        This is a regression test. It tries to PUT directly
+        credentials without the previous negotiation, so there is no
+        CredentialCache in the database. This attempt must fail.
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        request = self.app.get(
+            url="/delegation/%s/request" % creds.delegation_id, status=200
+        )
+        proxy = self.get_x509_proxy(request.body)
+
+        Session.delete(
+            Session.query(CredentialCache).get((creds.delegation_id, creds.user_dn))
+        )
+
+        self.app.put(
+            url="/delegation/%s/credential" % creds.delegation_id,
+            params=proxy,
+            status=400,
+        )
+
+    def test_put_malformed_pem(self):
+        """
+        Putting a malformed proxy must fail
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        self.app.get(url="/delegation/%s/request" % creds.delegation_id, status=200)
+
+        self.app.put(
+            url="/delegation/%s/credential" % creds.delegation_id,
+            params="MALFORMED!!!1",
+            status=400,
+        )
+
+    def test_valid_proxy(self):
+        """
+        Putting a well-formed proxy with all the right steps must succeed
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        request = self.app.get(
+            url="/delegation/%s/request" % creds.delegation_id, status=200
+        )
+        proxy = self.get_x509_proxy(request.body)
+
+        self.app.put(
+            url="/delegation/%s/credential" % creds.delegation_id,
+            params=proxy,
+            status=201,
+        )
+
+        proxy = Session.query(Credential).get((creds.delegation_id, creds.user_dn))
+        self.assertNotEqual(None, proxy)
+        return proxy
+
+    def test_dn_mismatch(self):
+        """
+        A well-formed proxy with mismatching issuer and subject must fail
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        request = self.app.get(
+            url="/delegation/%s/request" % creds.delegation_id, status=200
+        )
+
+        proxy = self.get_x509_proxy(request.body, subject=[("DC", "dummy")])
+
+        self.app.put(
+            url="/delegation/%s/credential" % creds.delegation_id,
+            params=proxy,
+            status=400,
+        )
+
+    def test_signed_wrong_priv_key(self):
+        """
+        Regression for FTS-30
+        If a proxy is signed with an invalid private key, reject it
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        request = self.app.get(
+            url="/delegation/%s/request" % creds.delegation_id, status=200
+        )
+
+        proxy = self.get_x509_proxy(request.body, private_key=EVP.PKey())
+
+        self.app.put(
+            url="/delegation/%s/credential" % creds.delegation_id,
+            params=proxy,
+            status=400,
+        )
+
+    def test_wrong_request(self):
+        """
+        Get a request, sign a different request and send it
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        self.app.get(url="/delegation/%s/request" % creds.delegation_id, status=200)
+
+        (different_request, _) = _generate_proxy_request()
+        proxy = self.get_x509_proxy(different_request.as_pem(), private_key=EVP.PKey())
+
+        self.app.put(
+            url="/delegation/%s/credential" % creds.delegation_id,
+            params=proxy,
+            status=400,
+        )
+
+    def test_get_request_different_dlg_id(self):
+        """
+        A user should be able only to get his/her own proxy request,
+        and be denied any other.
+        """
+        self.setup_gridsite_environment()
+
+        self.app.get(url="/delegation/12345xx/request", status=403)
+
+    def test_view_different_dlg_id(self):
+        """
+        A user should be able only to get his/her own delegation information.
+        """
+        self.setup_gridsite_environment()
+
+        self.app.get(url="/delegation/12345x", status=403)
+
+    def test_remove_delegation(self):
+        """
+        A user should be able to remove his/her proxy
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        self.test_valid_proxy()
+
+        self.app.delete(url="/delegation/%s" % creds.delegation_id, status=204)
+
+        self.app.delete(url="/delegation/%s" % creds.delegation_id, status=404)
+
+        proxy = Session.query(Credential).get((creds.delegation_id, creds.user_dn))
+
+        self.assertEqual(None, proxy)
+
+    def test_set_voms(self):
+        """
+        The server must regenerate a proxy with VOMS extensions
+        Need a real proxy for this one
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        # Need to push a real proxy :/
+        proxy_pem = self.get_real_x509_proxy()
+        if proxy_pem is None:
+            self.skipTest("Could not get a valid real proxy for test_set_voms")
+
+        proxy = Credential()
+        proxy.dn = creds.user_dn
+        proxy.dlg_id = creds.delegation_id
+        proxy.termination_time = datetime.utcnow() + timedelta(hours=1)
+        proxy.proxy = proxy_pem
+        Session.merge(proxy)
+        Session.commit()
+
+        # Now, request the voms extensions
+        self.app.post_json(
+            url="/delegation/%s/voms" % creds.delegation_id,
+            params=["dteam:/dteam/Role=lcgadmin"],
+            status=203,
+        )
+
+        # And validate
+        proxy2 = Session.query(Credential).get((creds.delegation_id, creds.user_dn))
+        self.assertNotEqual(proxy.proxy, proxy2.proxy)
+        self.assertEqual("dteam:/dteam/Role=lcgadmin", proxy2.voms_attrs)
+
+    def test_delegate_rfc(self):
+        """
+        Delegate an RFC-like proxy
+        """
+        self.setup_gridsite_environment()
+        creds = self.get_user_credentials()
+
+        request = self.app.get(
+            url="/delegation/%s/request" % creds.delegation_id, status=200
+        )
+
+        proxy = self.get_x509_proxy(
+            request.body,
+            subject=[
+                ("DC", "ch"),
+                ("DC", "cern"),
+                ("CN", "Test User"),
+                ("CN", str(int(time.time()))),
+            ],
+        )
+
+        self.app.put(
+            url="/delegation/%s/credential" % creds.delegation_id,
+            params=proxy,
+            status=201,
+        )
+
+        proxy = Session.query(Credential).get((creds.delegation_id, creds.user_dn))
+        self.assertNotEqual(None, proxy)
+
+    def test_cert(self, cert=None):
+        """
+        Test for returning the user certificate
+        """
+        self.setup_gridsite_environment()
+        if cert is None:
+            cert = "SSL_CLIENT_CERT"
+        self.app.environ_base["SSL_CLIENT_CERT"] = "certificate:" + cert
+
+        returns = self.app.get(url="/whoami/certificate", status=200).body
+        self.assertEqual("certificate:" + cert, returns)
-- 
GitLab


From 79db3bd728d2770e776b1d192f8f56f0b9983870 Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 10:44:03 +0200
Subject: [PATCH 2/9] fix

---
 .../fts3rest/tests/functional/test_delegation.py     | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/fts3rest/fts3rest/tests/functional/test_delegation.py b/src/fts3rest/fts3rest/tests/functional/test_delegation.py
index cd771a04..892da5ee 100644
--- a/src/fts3rest/fts3rest/tests/functional/test_delegation.py
+++ b/src/fts3rest/fts3rest/tests/functional/test_delegation.py
@@ -53,7 +53,7 @@ class TestDelegation(TestController):
         request = self.app.get(
             url="/delegation/%s/request" % creds.delegation_id, status=200
         )
-        proxy = self.get_x509_proxy(request.body)
+        proxy = self.get_x509_proxy(request.data)
 
         Session.delete(
             Session.query(CredentialCache).get((creds.delegation_id, creds.user_dn))
@@ -90,7 +90,7 @@ class TestDelegation(TestController):
         request = self.app.get(
             url="/delegation/%s/request" % creds.delegation_id, status=200
         )
-        proxy = self.get_x509_proxy(request.body)
+        proxy = self.get_x509_proxy(request.data)
 
         self.app.put(
             url="/delegation/%s/credential" % creds.delegation_id,
@@ -113,7 +113,7 @@ class TestDelegation(TestController):
             url="/delegation/%s/request" % creds.delegation_id, status=200
         )
 
-        proxy = self.get_x509_proxy(request.body, subject=[("DC", "dummy")])
+        proxy = self.get_x509_proxy(request.data, subject=[("DC", "dummy")])
 
         self.app.put(
             url="/delegation/%s/credential" % creds.delegation_id,
@@ -133,7 +133,7 @@ class TestDelegation(TestController):
             url="/delegation/%s/request" % creds.delegation_id, status=200
         )
 
-        proxy = self.get_x509_proxy(request.body, private_key=EVP.PKey())
+        proxy = self.get_x509_proxy(request.data, private_key=EVP.PKey())
 
         self.app.put(
             url="/delegation/%s/credential" % creds.delegation_id,
@@ -238,7 +238,7 @@ class TestDelegation(TestController):
         )
 
         proxy = self.get_x509_proxy(
-            request.body,
+            request.data,
             subject=[
                 ("DC", "ch"),
                 ("DC", "cern"),
@@ -265,5 +265,5 @@ class TestDelegation(TestController):
             cert = "SSL_CLIENT_CERT"
         self.app.environ_base["SSL_CLIENT_CERT"] = "certificate:" + cert
 
-        returns = self.app.get(url="/whoami/certificate", status=200).body
+        returns = self.app.get(url="/whoami/certificate", status=200).data
         self.assertEqual("certificate:" + cert, returns)
-- 
GitLab


From 2438692a05a1af8399aaa6c355f8a556d7a179cc Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 10:50:29 +0200
Subject: [PATCH 3/9] fix

---
 .../tests/functional/test_delegation.py        | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/fts3rest/fts3rest/tests/functional/test_delegation.py b/src/fts3rest/fts3rest/tests/functional/test_delegation.py
index 892da5ee..88f6c4b4 100644
--- a/src/fts3rest/fts3rest/tests/functional/test_delegation.py
+++ b/src/fts3rest/fts3rest/tests/functional/test_delegation.py
@@ -53,7 +53,7 @@ class TestDelegation(TestController):
         request = self.app.get(
             url="/delegation/%s/request" % creds.delegation_id, status=200
         )
-        proxy = self.get_x509_proxy(request.data)
+        proxy = self.get_x509_proxy(request.get_data(as_text=True))
 
         Session.delete(
             Session.query(CredentialCache).get((creds.delegation_id, creds.user_dn))
@@ -90,7 +90,7 @@ class TestDelegation(TestController):
         request = self.app.get(
             url="/delegation/%s/request" % creds.delegation_id, status=200
         )
-        proxy = self.get_x509_proxy(request.data)
+        proxy = self.get_x509_proxy(request.get_data(as_text=True))
 
         self.app.put(
             url="/delegation/%s/credential" % creds.delegation_id,
@@ -113,7 +113,9 @@ class TestDelegation(TestController):
             url="/delegation/%s/request" % creds.delegation_id, status=200
         )
 
-        proxy = self.get_x509_proxy(request.data, subject=[("DC", "dummy")])
+        proxy = self.get_x509_proxy(
+            request.get_data(as_text=True), subject=[("DC", "dummy")]
+        )
 
         self.app.put(
             url="/delegation/%s/credential" % creds.delegation_id,
@@ -133,7 +135,9 @@ class TestDelegation(TestController):
             url="/delegation/%s/request" % creds.delegation_id, status=200
         )
 
-        proxy = self.get_x509_proxy(request.data, private_key=EVP.PKey())
+        proxy = self.get_x509_proxy(
+            request.get_data(as_text=True), private_key=EVP.PKey()
+        )
 
         self.app.put(
             url="/delegation/%s/credential" % creds.delegation_id,
@@ -238,7 +242,7 @@ class TestDelegation(TestController):
         )
 
         proxy = self.get_x509_proxy(
-            request.data,
+            request.get_data(as_text=True),
             subject=[
                 ("DC", "ch"),
                 ("DC", "cern"),
@@ -265,5 +269,7 @@ class TestDelegation(TestController):
             cert = "SSL_CLIENT_CERT"
         self.app.environ_base["SSL_CLIENT_CERT"] = "certificate:" + cert
 
-        returns = self.app.get(url="/whoami/certificate", status=200).data
+        returns = self.app.get(url="/whoami/certificate", status=200).get_data(
+            as_text=True
+        )
         self.assertEqual("certificate:" + cert, returns)
-- 
GitLab


From aa3c8520e2e63706f81ab0c1f98f1bc2b439ff00 Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 10:54:42 +0200
Subject: [PATCH 4/9] fix pem should be bytes

---
 src/fts3rest/fts3rest/controllers/delegation.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/fts3rest/fts3rest/controllers/delegation.py b/src/fts3rest/fts3rest/controllers/delegation.py
index acd65d3c..a1ba5b67 100644
--- a/src/fts3rest/fts3rest/controllers/delegation.py
+++ b/src/fts3rest/fts3rest/controllers/delegation.py
@@ -112,7 +112,7 @@ def _validate_proxy(proxy_pem, private_key_pem):
     x509_proxy_issuer = x509_list[1]
 
     expiration_time = x509_proxy.get_not_after().get_datetime().replace(tzinfo=None)
-    private_key = EVP.load_key_string(str(private_key_pem), callback=_mute_callback)
+    private_key = EVP.load_key_string(private_key_pem, callback=_mute_callback)
 
     # The modulus of the stored private key and the modulus of the proxy must match
     if x509_proxy.get_pubkey().get_modulus() != private_key.get_modulus():
-- 
GitLab


From a3aab0cdf5b7f7b649498add1e44b25b5439fb6d Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 11:08:51 +0200
Subject: [PATCH 5/9] fix

---
 src/fts3rest/fts3rest/controllers/delegation.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/fts3rest/fts3rest/controllers/delegation.py b/src/fts3rest/fts3rest/controllers/delegation.py
index a1ba5b67..e7f58991 100644
--- a/src/fts3rest/fts3rest/controllers/delegation.py
+++ b/src/fts3rest/fts3rest/controllers/delegation.py
@@ -331,8 +331,12 @@ class credential(Delegation):
         log.debug("Received delegated credentials for %s" % dlg_id)
         log.debug(x509_proxy_pem)
 
+        if credential_cache.priv_key and isinstance(credential_cache.priv_key, str):
+            priv_key = bytes(credential_cache.priv_key, "utf-8")
+        else:
+            priv_key = credential_cache.priv_key
         try:
-            expiration_time = _validate_proxy(x509_proxy_pem, credential_cache.priv_key)
+            expiration_time = _validate_proxy(x509_proxy_pem, priv_key)
             x509_full_proxy_pem = _build_full_proxy(
                 x509_proxy_pem, credential_cache.priv_key
             )
-- 
GitLab


From faed09d5a8d360165dba58e7e2b44da733ea3257 Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 11:20:54 +0200
Subject: [PATCH 6/9] fix

---
 src/fts3rest/fts3rest/controllers/delegation.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/fts3rest/fts3rest/controllers/delegation.py b/src/fts3rest/fts3rest/controllers/delegation.py
index e7f58991..327adf0a 100644
--- a/src/fts3rest/fts3rest/controllers/delegation.py
+++ b/src/fts3rest/fts3rest/controllers/delegation.py
@@ -337,9 +337,7 @@ class credential(Delegation):
             priv_key = credential_cache.priv_key
         try:
             expiration_time = _validate_proxy(x509_proxy_pem, priv_key)
-            x509_full_proxy_pem = _build_full_proxy(
-                x509_proxy_pem, credential_cache.priv_key
-            )
+            x509_full_proxy_pem = _build_full_proxy(x509_proxy_pem, priv_key)
         except ProxyException as ex:
             raise BadRequest("Could not process the proxy: " + str(ex))
 
-- 
GitLab


From 5c77ac7a6cb14c482f5d592d365bed898255ed89 Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 11:24:31 +0200
Subject: [PATCH 7/9] fix

---
 src/fts3rest/fts3rest/controllers/delegation.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/fts3rest/fts3rest/controllers/delegation.py b/src/fts3rest/fts3rest/controllers/delegation.py
index 327adf0a..16ef9f24 100644
--- a/src/fts3rest/fts3rest/controllers/delegation.py
+++ b/src/fts3rest/fts3rest/controllers/delegation.py
@@ -153,7 +153,7 @@ def _build_full_proxy(x509_pem, privkey_pem):
         A full proxy
     """
     x509_list = _read_x509_list(x509_pem)
-    x509_chain = "".join(map(lambda x: x.as_pem(), x509_list[1:]))
+    x509_chain = b"".join(map(lambda x: x.as_pem(), x509_list[1:]))
     return x509_list[0].as_pem() + privkey_pem + x509_chain
 
 
-- 
GitLab


From d008b694181adf79b9a68e0e15ab7f14ea61563f Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 11:33:01 +0200
Subject: [PATCH 8/9] fix

---
 src/fts3rest/fts3rest/tests/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/fts3rest/fts3rest/tests/__init__.py b/src/fts3rest/fts3rest/tests/__init__.py
index c031d6af..ee046677 100644
--- a/src/fts3rest/fts3rest/tests/__init__.py
+++ b/src/fts3rest/fts3rest/tests/__init__.py
@@ -129,7 +129,7 @@ class TestController(TestCase):
         if subject is None:
             subject = issuer + [("CN", "proxy")]
 
-        x509_request = X509.load_request_string(str(request_pem))
+        x509_request = X509.load_request_string(request_pem)
 
         not_before = ASN1.ASN1_UTCTIME()
         not_before.set_datetime(datetime.now(UTC))
-- 
GitLab


From 913e0fea085d467b91c2266c172ad2115245b115 Mon Sep 17 00:00:00 2001
From: Carles Garcia Cabot <carles.garcia.cabot@cern.ch>
Date: Thu, 9 Apr 2020 12:09:00 +0200
Subject: [PATCH 9/9] change test assert from None to []

---
 src/fts3rest/fts3rest/tests/functional/test_delegation.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/fts3rest/fts3rest/tests/functional/test_delegation.py b/src/fts3rest/fts3rest/tests/functional/test_delegation.py
index 88f6c4b4..8dae6d96 100644
--- a/src/fts3rest/fts3rest/tests/functional/test_delegation.py
+++ b/src/fts3rest/fts3rest/tests/functional/test_delegation.py
@@ -23,7 +23,9 @@ class TestDelegation(TestController):
         delegation_id = self.app.get(
             url="/delegation/%s" % creds.delegation_id, status=200
         ).json
-        self.assertEqual(delegation_id, None)
+        self.assertEqual(
+            delegation_id, []
+        )  # for Flask, I changed None to [], as it makes more sense
 
     def test_get_termination_time(self):
         """
-- 
GitLab