From afbdc84f84e63b3714e257153335114c59e4c1f4 Mon Sep 17 00:00:00 2001
From: Captain_Trojan <sejakm@students.zcu.cz>
Date: Fri, 16 Apr 2021 22:06:28 +0200
Subject: [PATCH 1/3] Re #8574 - Extended the `get_certificate_list` method of
 CertificateController to reflect the filtering query 'issuedby' update.

---
 src/controllers/certificates_controller.py | 34 +++++++++++++++-------
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/src/controllers/certificates_controller.py b/src/controllers/certificates_controller.py
index 932c745..67b5004 100644
--- a/src/controllers/certificates_controller.py
+++ b/src/controllers/certificates_controller.py
@@ -29,6 +29,7 @@ USAGE = "usage"
 SUBJECT = "subject"
 VALIDITY_DAYS = "validityDays"
 CA = "CA"
+ISSUED_BY = "issuedby"
 STATUS = "status"
 REASON = "reason"
 REASON_UNDEFINED = "unspecified"
@@ -161,7 +162,7 @@ class CertController:
         cert = self.certificate_service.get_certificate(v)
 
         if cert is None:
-            return E_NO_CERTIFICATES_FOUND, C_NO_DATA                               
+            return E_NO_CERTIFICATES_FOUND, C_NO_DATA
         else:
             return {"success": True, "data": cert.pem_data}, C_SUCCESS
 
@@ -183,7 +184,7 @@ class CertController:
         cert = self.certificate_service.get_certificate(v)
 
         if cert is None:
-            return E_NO_CERTIFICATES_FOUND, C_NO_DATA                               
+            return E_NO_CERTIFICATES_FOUND, C_NO_DATA
         else:
             data = self.cert_to_dict_full(cert)
             if data is None:
@@ -201,14 +202,15 @@ class CertController:
         :rtype: CertificateListResponse
         """
         targets = {ROOT_CA_ID, INTERMEDIATE_CA_ID, CERTIFICATE_ID}                  # all targets
+        issuer_id = -1
 
         # the filtering parameter can be read as URL argument or as a request body
-        if request.is_json or "filtering" in request.args.keys():                   # if the request carries JSON data
+        if request.is_json or FILTERING in request.args.keys():                     # if the request carries JSON data
             if request.is_json:
                 data = request.get_json()                                           # get it
             else:
                 try:
-                    data = {"filtering": json.loads(request.args["filtering"])}
+                    data = {FILTERING: json.loads(request.args[FILTERING])}
                 except JSONDecodeError:
                     return E_NOT_JSON_FORMAT, C_BAD_REQUEST
 
@@ -223,23 +225,33 @@ class CertController:
                                 targets.remove(INTERMEDIATE_CA_ID)
                         else:
                             return E_WRONG_PARAMETERS, C_BAD_REQUEST
+                    if ISSUED_BY in data[FILTERING]:                                # containing 'issuedby'
+                        if isinstance(data[FILTERING][ISSUED_BY], int):             # which is an 'int'
+                            issuer_id = data[FILTERING][ISSUED_BY]                  # then get its children only
                 else:
                     return E_WRONG_PARAMETERS, C_BAD_REQUEST
+            if issuer_id >= 0:                                                      # if filtering by an issuer
+                try:
+                    children = self.certificate_service.get_certificates_issued_by(issuer_id)  # get his children
+                except CertificateNotFoundException:                                # if id does not exist
+                    return E_NO_CERTIFICATES_FOUND, C_NOT_FOUND                     # throw
 
-        if len(targets) == TREE_NODE_TYPE_COUNT:                                    # = 3 -> root node,
+                certs = [child for child in children if child.type_id in targets]
+
+            elif len(targets) == TREE_NODE_TYPE_COUNT:                              # = 3 -> root node,
                                                                                     # intermediate node,
                                                                                     # or leaf node
 
                                                                                     # if filtering did not change the
                                                                                     # targets,
-            certs = self.certificate_service.get_certificates()                          # fetch everything
-        else:                                                                       # otherwise fetch targets only
-            certs = list(chain(*(self.certificate_service.get_certificates(target) for target in targets)))
+                certs = self.certificate_service.get_certificates()                 # fetch everything
+            else:                                                                   # otherwise fetch targets only
+                certs = list(chain(*(self.certificate_service.get_certificates(target) for target in targets)))
+        else:
+            certs = self.certificate_service.get_certificates()                     # if no params, fetch everything
 
         if certs is None:
             return E_GENERAL_ERROR, C_INTERNAL_SERVER_ERROR
-        elif len(certs) == 0:
-            return E_NO_CERTIFICATES_FOUND, C_NO_DATA                               
         else:
             ret = []
             for c in certs:
@@ -296,7 +308,7 @@ class CertController:
         cert = self.certificate_service.get_certificate(v)
 
         if cert is None:
-            return E_NO_CERTIFICATES_FOUND, C_NO_DATA                               
+            return E_NO_CERTIFICATES_FOUND, C_NO_DATA
 
         if cert.parent_id is None:
             return E_NO_CERTIFICATES_FOUND, C_NO_DATA
-- 
GitLab


From 485913d0a75f4e733b19be3e542939919f0bd81b Mon Sep 17 00:00:00 2001
From: Captain_Trojan <sejakm@students.zcu.cz>
Date: Fri, 16 Apr 2021 22:08:43 +0200
Subject: [PATCH 2/3] Re #8574 - Added the `get_certificates_issued_by(id)`
 method to the CertificateService (just calling the homonymous CryptoService
 method).

---
 src/services/certificate_service.py | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/services/certificate_service.py b/src/services/certificate_service.py
index a609443..5dd997d 100644
--- a/src/services/certificate_service.py
+++ b/src/services/certificate_service.py
@@ -6,6 +6,7 @@ from src.config.configuration import Configuration
 from src.constants import ROOT_CA_ID, INTERMEDIATE_CA_ID, CA_ID, CERTIFICATE_ID, CERTIFICATE_STATES, \
     CERTIFICATE_REVOCATION_REASONS
 from src.dao.certificate_repository import CertificateRepository
+from src.exceptions.database_exception import DatabaseException
 from src.exceptions.unknown_exception import UnknownException
 from src.model.certificate import Certificate
 from src.model.private_key import PrivateKey
@@ -267,6 +268,21 @@ class CertificateService:
         # TODO delete children?
         return self.certificate_repository.delete(unique_id)
 
+    def get_certificates_issued_by(self, unique_id):
+        """
+        Returns a list of all children of a certificate identified by an unique_id.
+        Raises a DatabaseException should any unexpected behavior occur.
+        :param unique_id: target certificate ID
+        :return: children of unique_id
+        """
+        try:
+            if self.certificate_repository.read(unique_id) is None:
+                raise CertificateNotFoundException(unique_id)
+        except DatabaseException:
+            raise CertificateNotFoundException(unique_id)
+
+        return self.certificate_repository.get_all_issued_by(unique_id)
+
     def set_certificate_revocation_status(self, id, status, reason="unspecified"):
         """
         Set certificate status to 'valid' or 'revoked'.
-- 
GitLab


From 5aa1b47663ba8591f85be7637d149ecb4521f18a Mon Sep 17 00:00:00 2001
From: Captain_Trojan <sejakm@students.zcu.cz>
Date: Fri, 16 Apr 2021 22:09:31 +0200
Subject: [PATCH 3/3] Re #8574 - Added tests for the new filtering
 functionality.

---
 .../rest_api/certificates_test.py              | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/tests/integration_tests/rest_api/certificates_test.py b/tests/integration_tests/rest_api/certificates_test.py
index a63dad1..9d68c62 100644
--- a/tests/integration_tests/rest_api/certificates_test.py
+++ b/tests/integration_tests/rest_api/certificates_test.py
@@ -399,6 +399,24 @@ def test_filtering(server):
     assert len(all_certs_actual) == len(all_certs_expected)
     # TODO assert set(all_certs_expected) == set(all_certs_actual) or something like that instead
 
+    ret = server.get("/api/certificates", json={"filtering": {"issuedby": 2}})
+    assert ret.status_code == 200
+    assert "data" in ret.json
+    assert "success" in ret.json
+    assert ret.json["success"]
+    issued_by_2 = ret.json["data"]
+    assert len(issued_by_2) == 2
+    assert set(issued_by_2[i]["id"] for i in range(2)) == {3, 8}
+
+    ret = server.get("/api/certificates", json={"filtering": {"issuedby": 2, "CA": True}})
+    assert ret.status_code == 200
+    assert "data" in ret.json
+    assert "success" in ret.json
+    assert ret.json["success"]
+    issued_by_2 = ret.json["data"]
+    assert len(issued_by_2) == 1
+    assert issued_by_2[0]["id"] == 3
+
 
 def test_get_one(server):
     ret = server.get("/api/certificates/1")
-- 
GitLab