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