From 76233471634c54e0d4c1844448f5b0d0411a090d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Pitucha?= Date: Fri, 3 Jul 2015 15:07:35 +1000 Subject: [PATCH] Return CA for a given instance Return the signing CA certificate. Closes bug: 1409037 Change-Id: I57ec9b657dd2bedf4b13a45703f7fd0c6d1d4a0b --- anchor/certificate_ops.py | 11 +++++++++++ anchor/controllers/__init__.py | 27 +++++++++++++++++++++------ doc/source/api.rst | 20 ++++++++++++++++++++ tests/test_functional.py | 13 +++++++++++++ 4 files changed, 65 insertions(+), 6 deletions(-) diff --git a/anchor/certificate_ops.py b/anchor/certificate_ops.py index 92c4987..01b318f 100644 --- a/anchor/certificate_ops.py +++ b/anchor/certificate_ops.py @@ -133,6 +133,17 @@ def certificate_fingerprint(cert_pem, hash_name): return cert.get_fingerprint(hash_name) +def get_ca(ra_name): + ca_conf = jsonloader.signing_ca_for_registration_authority(ra_name) + + ca_path = ca_conf.get('cert_path') + if not ca_path: + pecan.abort(404, "CA certificate not available") + + with open(ca_path) as f: + return f.read() + + def dispatch_sign(ra_name, csr): """Dispatch the sign call to the configured backend. diff --git a/anchor/controllers/__init__.py b/anchor/controllers/__init__.py index c164b86..f1f18dd 100644 --- a/anchor/controllers/__init__.py +++ b/anchor/controllers/__init__.py @@ -32,12 +32,14 @@ class RobotsController(rest.RestController): return "User-agent: *\nDisallow: /\n" -class SignInstanceController(rest.RestController): - """Handles POST requests to /v1/sign/ra_name.""" - +class GenericInstanceController(rest.RestController): + """Handles requests to /xxx/ra_name.""" def __init__(self, ra_name): self.ra_name = ra_name + +class SignInstanceController(GenericInstanceController): + """Handles POST requests to /sign/instance.""" @pecan.expose(content_type="text/plain") def post(self): ra_name = self.ra_name @@ -54,16 +56,29 @@ class SignInstanceController(rest.RestController): return certificate_ops.dispatch_sign(ra_name, csr) -class SignController(rest.RestController): +class CAInstanceController(GenericInstanceController): + """Handles POST requests to /ca/ra_name.""" + @pecan.expose(content_type="text/plain") + def get(self): + ra_name = self.ra_name + + return certificate_ops.get_ca(ra_name) + + +class RAController(rest.RestController): + def __init__(self, subcontroller): + self._subcontroller = subcontroller + @pecan.expose() def _lookup(self, ra_name, *remaining): if ra_name in jsonloader.registration_authority_names(): - return SignInstanceController(ra_name), remaining + return self._subcontroller(ra_name), remaining pecan.abort(404) class V1Controller(rest.RestController): - sign = SignController() + sign = RAController(SignInstanceController) + ca = RAController(CAInstanceController) class RootController(object): diff --git a/doc/source/api.rst b/doc/source/api.rst index 2f97c19..ad3c538 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -26,3 +26,23 @@ Result ~~~~~~ Signed certificate + +/v1/ca/ (GET) +---------------------------------------- + +Requests the CA which would be used to sign the request made to this +registration authority. + +Do *NOT* use this to retrieve the certificate needed to trust communication +with Anchor. Connection to Anchor *MUST* be fully trusted before using this +endpoint for further configuration. + +Request parameters +~~~~~~~~~~~~~~~~~~ + +* ``encoding``: request encoding - currently supported: "pem" + +Result +~~~~~~ + +CA certificate used to sign for this RA. diff --git a/tests/test_functional.py b/tests/test_functional.py index cb2a094..b1035f9 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py @@ -173,3 +173,16 @@ class TestFunctional(tests.DefaultConfigMixin, unittest.TestCase): "'broken_validator' for registration authority " "'default_ra'") in str(resp)) self.assertTrue(derp.called) + + def test_get_ca(self): + data = {'encoding': 'pem'} + + resp = self.app.get('/v1/ca/default_ra', data, expect_errors=False) + self.assertEqual(200, resp.status_int) + + cert = X509_cert.X509Certificate.from_buffer(resp.text) + + # make sure the cert is what we asked for + self.assertEqual("/C=AU/ST=Some-State/O=Herp Derp plc/OU" + "=herp.derp.plc/CN=herp.derp.plc", + str(cert.get_subject()))