commit 5b6d95130b59157c9caa6f6f1c9a8b7996797329
Author: Captain_Trojan <sejakm@students.zcu.cz>
Date:   Wed Apr 7 01:44:03 2021 +0200

    Re #8476 - Implemented and tested `get_cert_details(id)`.

diff --git a/app.py b/app.py
index d48e539..3ec4e6b 100644
--- a/app.py
+++ b/app.py
@@ -25,6 +25,11 @@ def get_cert(id):
     return CertController.get_certificate_by_id(id)
 
 
+@app.route('/api/certificates/<id>/details', methods=["GET"])
+def get_cert_detes(id):
+    return CertController.get_certificate_details_by_id(id)
+
+
 if __name__ == '__main__':
     host = "0.0.0.0"
     port = 5000
diff --git a/src/controllers/certificates_controller.py b/src/controllers/certificates_controller.py
index 59d76a5..d4ad6a5 100644
--- a/src/controllers/certificates_controller.py
+++ b/src/controllers/certificates_controller.py
@@ -50,6 +50,8 @@ KEY_SERVICE = KeyService(__, PrivateKeyRepository(None, None))  # TODO as above
 
 
 class CertController:
+    KEY_MAP = {'CA': CA_ID, 'SSL': SSL_ID, 'digitalSignature': SIGNATURE_ID, 'authentication': AUTHENTICATION_ID}
+    INVERSE_KEY_MAP = {k: v for v, k in KEY_MAP.items()}
 
     @staticmethod
     def setup():
@@ -76,7 +78,6 @@ class CertController:
         """
         CertController.setup()  # TODO remove after issue fixed
 
-        key_map = {'CA': CA_ID, 'SSL': SSL_ID, 'digitalSignature': SIGNATURE_ID, 'authentication': AUTHENTICATION_ID}
         required_keys = {SUBJECT, USAGE, VALIDITY_DAYS}
 
         if request.is_json:
@@ -92,15 +93,15 @@ class CertController:
             if subject is None:
                 return E_WRONG_PARAMETERS, 400
 
-            usages_dict = DICT_USAGES.copy()
+            usages_dict = {}
 
             if not isinstance(body[USAGE], dict):
                 return E_WRONG_PARAMETERS, 400
 
             for k, v in body[USAGE].items():
-                if k not in key_map:
+                if k not in CertController.KEY_MAP:
                     return E_WRONG_PARAMETERS, 400
-                usages_dict[key_map[k]] = v
+                usages_dict[CertController.KEY_MAP[k]] = v
 
             key = KEY_SERVICE.create_new_key()  # TODO pass key
 
@@ -115,11 +116,13 @@ class CertController:
                 issuer = CERTIFICATE_SERVICE.get_certificate(body[CA])
 
                 if issuer is None:
+                    KEY_SERVICE.delete_key(key.private_key_id)
                     return E_NO_ISSUER_FOUND, 400
 
                 issuer_key = KEY_SERVICE.get_key(issuer.private_key_id)
 
                 if issuer_key is None:
+                    KEY_SERVICE.delete_key(key.private_key_id)
                     return E_CORRUPTED_DATABASE, 500
 
                 f = CERTIFICATE_SERVICE.create_ca if CA_ID in usages_dict and usages_dict[CA_ID] else \
@@ -138,6 +141,7 @@ class CertController:
                 return {"success": True,
                         "data": cert.certificate_id}, 201
             else:
+                KEY_SERVICE.delete_key(key.private_key_id)
                 return {"success": False,
                         "data": "Internal error: The certificate could not have been created."}, 400
         else:
@@ -181,9 +185,20 @@ class CertController:
         """
         CertController.setup()  # TODO remove after issue fixed
 
-        if connexion.request.is_json:
-            id = IdParameter.from_dict(connexion.request.get_json())  # noqa: E501
-        return 'do some magic!'
+        try:
+            v = int(id)
+        except ValueError:
+            return E_WRONG_PARAMETERS, 400
+
+        cert = CERTIFICATE_SERVICE.get_certificate(v)
+
+        if cert is None:
+            return E_NO_CERTIFICATES_FOUND, 205  # TODO related to 204 issue
+        else:
+            data = CertController.cert_to_dict_full(cert)
+            if data is None:
+                return E_CORRUPTED_DATABASE, 500
+            return {"success": True, "data": data}
 
     @staticmethod
     def get_certificate_list():  # noqa: E501
@@ -198,8 +213,6 @@ class CertController:
         """
         CertController.setup()  # TODO remove after issue fixed
 
-        key_map = {CA_ID: 'CA', SSL_ID: 'SSL', SIGNATURE_ID: 'digitalSignature', AUTHENTICATION_ID: 'authentication'}
-
         targets = {ROOT_CA_ID, INTERMEDIATE_CA_ID, CERTIFICATE_ID}
         if request.is_json:
             data = request.get_json()
@@ -217,7 +230,7 @@ class CertController:
                 else:
                     return E_WRONG_PARAMETERS, 400
 
-        if len(targets) == 3:
+        if len(targets) == 3:                               # potentially risky
             certs = CERTIFICATE_SERVICE.get_certificates()
         else:
             certs = list(chain(*(CERTIFICATE_SERVICE.get_certificates(target) for target in targets)))
@@ -229,27 +242,15 @@ class CertController:
         else:
             ret = []
             for c in certs:
-                c_issuer = CERTIFICATE_SERVICE.get_certificate(c.parent_id)
-                if c_issuer is None:
+                data = CertController.cert_to_dict_partial(c)
+                if data is None:
                     return E_CORRUPTED_DATABASE, 500
-
                 ret.append(
-                    {
-                        ID: c.certificate_id,
-                        COMMON_NAME: c.common_name,
-                        NOT_BEFORE: datetime.strptime(c.valid_from, DATETIME_FORMAT).date(),
-                        NOT_AFTER: datetime.strptime(c.valid_to, DATETIME_FORMAT).date(),
-                        USAGE: {key_map[k]: v for k, v in c.usages.items()},
-                        ISSUER: {
-                            ID: c_issuer.certificate_id,
-                            COMMON_NAME: c_issuer.common_name
-                        }
-                    }
+                    data
                 )
             return {"success": True, "data": ret}
 
 
-
     @staticmethod
     def get_certificate_root_by_id(id):  # noqa: E501
         """get certificate&#x27;s root of trust chain by ID
@@ -283,3 +284,37 @@ class CertController:
         if connexion.request.is_json:
             id = IdParameter.from_dict(connexion.request.get_json())  # noqa: E501
         return 'do some magic!'
+
+
+    @staticmethod
+    def cert_to_dict_partial(c):
+        c_issuer = CERTIFICATE_SERVICE.get_certificate(c.parent_id)
+        if c_issuer is None:
+            return None
+
+        return {
+            ID: c.certificate_id,
+            COMMON_NAME: c.common_name,
+            NOT_BEFORE: datetime.strptime(c.valid_from, DATETIME_FORMAT).date(),
+            NOT_AFTER: datetime.strptime(c.valid_to, DATETIME_FORMAT).date(),
+            USAGE: {CertController.INVERSE_KEY_MAP[k]: v for k, v in c.usages.items()},
+            ISSUER: {
+                ID: c_issuer.certificate_id,
+                COMMON_NAME: c_issuer.common_name
+            }
+        }
+
+    @staticmethod
+    def cert_to_dict_full(c):
+        subj = CERTIFICATE_SERVICE.get_subject_from_certificate(c)
+        c_issuer = CERTIFICATE_SERVICE.get_certificate(c.parent_id)
+        if c_issuer is None:
+            return None
+
+        return {
+            SUBJECT: subj.to_dict(),
+            NOT_BEFORE: datetime.strptime(c.valid_from, DATETIME_FORMAT).date(),
+            NOT_AFTER: datetime.strptime(c.valid_to, DATETIME_FORMAT).date(),
+            USAGE: {CertController.INVERSE_KEY_MAP[k]: v for k, v in c.usages.items()},
+            CA: c_issuer.certificate_id
+        }
diff --git a/tests/unit_tests/rest_api/certificates.py b/tests/unit_tests/rest_api/certificates.py
index c5162b2..ce5f9bc 100644
--- a/tests/unit_tests/rest_api/certificates.py
+++ b/tests/unit_tests/rest_api/certificates.py
@@ -370,4 +370,55 @@ def test_get_one_bad_param_2(server):
     assert "data" in ret.json
     assert "success" in ret.json
     assert not ret.json["success"]
-    assert ret.json["data"] == "No certificates found."
\ No newline at end of file
+    assert ret.json["data"] == "No certificates found."
+
+
+def test_get_detes(server):
+    original = {
+        "CA": 1,
+        "subject": {
+            "C": "CZ",
+            "CN": "Good boy end cert s.r.o.",
+            "L": "Pilsen",
+            "O": "This is most likely a good s.r.o.",
+            "OU": "IT department",
+            "ST": "Pilsen Region",
+            "emailAddress": "end@ca.com"
+        },
+        "usage": {
+            "CA": False,
+            "SSL": True,
+            "authentication": False,
+            "digitalSignature": True
+        },
+        "validityDays": 30
+    }
+    ret = server.post("/api/certificates", content_type="application/json", json=original)
+
+    assert ret.status_code == 201
+
+    assert "data" in ret.json
+    assert "success" in ret.json
+    assert ret.json["success"]
+
+    d = ret.json
+
+    assert d["data"] == 6
+
+    id = d["data"]
+
+    ret = server.get(f"/api/certificates/{id}/details")
+
+    assert ret.status_code == 200
+
+    assert "data" in ret.json
+    assert "success" in ret.json
+    assert ret.json["success"]
+
+    d = ret.json
+
+    new = d["data"]
+
+    assert original["CA"] == new["CA"]
+    assert original["subject"] == new["subject"]
+    assert original["usage"] == new["usage"]
\ No newline at end of file
