commit dd6b4c4c6715e8d84d725ef7f6cca734ab4574d2
Author: Jan Pašek <janpasek97@gmail.com>
Date:   Wed Apr 21 12:46:30 2021 +0200

    Re #8576 - Added crl endpoint integration tests, fix of serial number

diff --git a/src/services/crl/ca_index_file_line_generator.py b/src/services/crl/ca_index_file_line_generator.py
index 2018a9e..8804ce3 100644
--- a/src/services/crl/ca_index_file_line_generator.py
+++ b/src/services/crl/ca_index_file_line_generator.py
@@ -4,6 +4,7 @@ from time import struct_time
 from src.model.certificate import Certificate
 from src.model.subject import Subject
 
+SRL_LEN = 8  # number of hex digits in serial number
 TAB_CHAR = "\t"
 INDEX_FILE_DATE_ENTRY_FORMAT = "%y%m%d%H%M%SZ"
 
@@ -30,7 +31,7 @@ def create_index_file_revoked_line(revoked_certificate: Certificate, subject: Su
         # followed by the revocation date field
         f"{get_index_file_time_entry(revocation_date)},{revoked_certificate.revocation_reason}",
         # followed by the serial number of the certificate in hex format
-        hex(revoked_certificate.certificate_id).replace("x", "").upper(),
+        __get_serial(revoked_certificate.certificate_id),
         # certificate filename ("unknown" literal used for unknown file names)
         "unknown",
         # certificate distinguished name
@@ -50,11 +51,16 @@ def create_index_file_valid_line(certificate: Certificate, subject: Subject, val
         # followed by the revocation date field
         f"",
         # followed by the serial number of the certificate in hex format
-        hex(certificate.certificate_id).replace("x", "").upper(),
+        __get_serial(certificate.certificate_id),
         # certificate filename ("unknown" literal used for unknown file names)
         "unknown",
         # certificate distinguished name
         get_distinguished_name(subject)
     ]
 
-    return TAB_CHAR.join(items)
\ No newline at end of file
+    return TAB_CHAR.join(items)
+
+def __get_serial(cert_id) -> str:
+    srl = hex(cert_id).replace("0x", "")
+    srl = "0"*(SRL_LEN - len(srl)) + srl  # generate exactly SRL_LEN digits
+    return srl.upper()
diff --git a/tests/integration_tests/rest_api/certificates_test.py b/tests/integration_tests/rest_api/certificates_test.py
index 6fa11c9..c3efc43 100644
--- a/tests/integration_tests/rest_api/certificates_test.py
+++ b/tests/integration_tests/rest_api/certificates_test.py
@@ -831,4 +831,4 @@ def test_certificate_deletion_invalid_params_1(server):
 def test_certificate_deletion_invalid_params_2(server):
     ret = server.delete("/api/certificates/a_big_number")
     assert ret.status_code == 400
-    assert not ret.json["success"]
\ No newline at end of file
+    assert not ret.json["success"]
diff --git a/tests/integration_tests/rest_api/crl_ocsp_test.py b/tests/integration_tests/rest_api/crl_ocsp_test.py
new file mode 100644
index 0000000..df48586
--- /dev/null
+++ b/tests/integration_tests/rest_api/crl_ocsp_test.py
@@ -0,0 +1,108 @@
+
+def make_root_ca(server, title="Root CA s.r.o."):
+    return server.post("/api/certificates", content_type="application/json", json={
+        "subject": {
+            "C": "CZ",
+            "CN": title,
+            "L": "Pilsen",
+            "O": title,
+            "OU": "IT department",
+            "ST": "Pilsen Region",
+            "emailAddress": "root@ca.com"
+        },
+        "usage": {
+            "CA": True,
+            "SSL": True,
+            "authentication": True,
+            "digitalSignature": True
+        },
+        "validityDays": 30
+    })
+
+
+def make_end_cert(server, parent, title="End certificate s.r.o.", usage=None):
+    if usage is None:
+        usage = {
+            "CA": False,
+            "SSL": True,
+            "authentication": True,
+            "digitalSignature": True
+        }
+
+    return server.post("/api/certificates", content_type="application/json", json={
+        "CA": parent,
+        "subject": {
+            "C": "CZ",
+            "CN": title,
+            "L": "Pilsen",
+            "O": title,
+            "OU": "IT department",
+            "ST": "Pilsen Region",
+            "emailAddress": "end@ca.com"
+        },
+        "usage": usage,
+        "validityDays": 30
+    })
+
+
+def test_crl_endpoint_empty(server):
+    ret = make_root_ca(server, title="Root 1")
+    data = ret.json
+    root_id = data["data"]
+
+    ret = make_end_cert(server, root_id, title="End1")
+    data = ret.json
+    end1_id = data["data"]
+
+    ret = make_end_cert(server, root_id, title="End2")
+    data = ret.json
+    end2_id = data["data"]
+
+    ret = server.get(f"/api/crl/{root_id}")
+    assert ret.status_code == 200
+    assert ret.content_type == "application/x-x509-ca-cert"
+    assert "BEGIN X509 CRL" in ret.data.decode("utf-8")
+
+
+def test_crl_revoked_certificates_exist(server):
+    ret = make_root_ca(server, title="Root 1")
+    data = ret.json
+    root_id = data["data"]
+
+    ret = make_end_cert(server, root_id, title="End1")
+    data = ret.json
+    end1_id = data["data"]
+
+    ret = make_end_cert(server, root_id, title="End2")
+    data = ret.json
+    end2_id = data["data"]
+
+    # revoke the certificate end2
+    revocation_body = {
+        "status": "revoked",
+        "reason": "keyCompromise"
+    }
+    revoke_ret = server.patch(f"/api/certificates/{end2_id}", content_type="application/json", json=revocation_body)
+    assert revoke_ret.status_code == 200
+    assert revoke_ret.json["data"]
+
+    # revoke the certificate end2
+    revocation_body = {
+        "status": "revoked",
+        "reason": "certificateHold"
+    }
+    revoke_ret = server.patch(f"/api/certificates/{end1_id}", content_type="application/json", json=revocation_body)
+    assert revoke_ret.status_code == 200
+    assert revoke_ret.json["data"]
+
+    ret = server.get(f"/api/crl/{root_id}")
+    assert ret.status_code == 200
+    assert ret.content_type == "application/x-x509-ca-cert"
+    assert "BEGIN X509 CRL" in ret.data.decode("utf-8")
+
+
+def test_crl_invalid_id(server):
+    ret = server.get("/api/crl/888")
+    assert ret.status_code == 404
+
+
diff --git a/tests/integration_tests/services/crl_service_test.py b/tests/integration_tests/services/crl_service_test.py
index c847755..8f64cca 100644
--- a/tests/integration_tests/services/crl_service_test.py
+++ b/tests/integration_tests/services/crl_service_test.py
@@ -91,10 +91,10 @@ def test_set_certificate_revoked(certificate_service_unique, private_key_service
 
     # arrange expected lines
     expected_lines = [
-        f"V\t{valid_to_dates[0]}\t\t01\tunknown\t/CN=RootFoo/OU=Department of Foo",
-        f"R	{valid_to_dates[1]}	{revoked_dates[0]},unspecified	02	unknown	/CN=Foo CA/L=Brno",
-        f"R	{valid_to_dates[2]}	{revoked_dates[1]},keyCompromise	03	unknown	/CN=Bar CA/C=CZ/L=Pilsen",
-        f"R	{valid_to_dates[3]}	{revoked_dates[2]},CACompromise	05	unknown	/CN=Baz CA/ST=ST"
+        f"V\t{valid_to_dates[0]}\t\t00000001\tunknown\t/CN=RootFoo/OU=Department of Foo",
+        f"R	{valid_to_dates[1]}	{revoked_dates[0]},unspecified	00000002	unknown	/CN=Foo CA/L=Brno",
+        f"R	{valid_to_dates[2]}	{revoked_dates[1]},keyCompromise	00000003	unknown	/CN=Bar CA/C=CZ/L=Pilsen",
+        f"R	{valid_to_dates[3]}	{revoked_dates[2]},CACompromise	00000005	unknown	/CN=Baz CA/ST=ST"
     ]
 
     assert out == "\n".join(expected_lines)
diff --git a/tests/unit_tests/services/crl/ca_index_file_line_generator_test.py b/tests/unit_tests/services/crl/ca_index_file_line_generator_test.py
index 7b2d878..c059ebe 100644
--- a/tests/unit_tests/services/crl/ca_index_file_line_generator_test.py
+++ b/tests/unit_tests/services/crl/ca_index_file_line_generator_test.py
@@ -19,7 +19,7 @@ def test_get_index_file_time_entry_valid():
         "Foo", "CZ", "Pilsen", email_address="bar@foo.cz"
     ), revocation_date, expiration_date)
 
-    assert "R	210321000000Z	210202000000Z,keyCompromise	01	unknown	" \
+    assert "R	210321000000Z	210202000000Z,keyCompromise	00000001	unknown	" \
            "/CN=Foo/C=CZ/L=Pilsen/emailAddress=bar@foo.cz" \
            == index_line
 
@@ -38,7 +38,7 @@ def test_get_index_file_time_entry_valid_2():
         "Bar", "SK", "Foosen", organization="Bar Org."
     ), revocation_date, expiration_date)
 
-    assert "R	211006000000Z	210903000000Z,affiliationChanged	018AA0	unknown	" \
+    assert "R	211006000000Z	210903000000Z,affiliationChanged	00018AA0	unknown	" \
            "/CN=Bar/C=SK/L=Foosen/O=Bar Org." \
            == index_line
 
@@ -60,7 +60,7 @@ def test_get_index_file_time_entry_valid_3():
         organization="Internet Widgits Pty Ltd "
     ), revocation_date, expiration_date)
 
-    expected = "R	230401150111Z	210401151200Z,keyCompromise	01	unknown	/CN=IA CA/C=AU/ST=Some-State/O=Internet " \
+    expected = "R	230401150111Z	210401151200Z,keyCompromise	00000001	unknown	/CN=IA CA/C=AU/ST=Some-State/O=Internet " \
                "Widgits Pty Ltd "
 
     assert expected == index_line
@@ -81,6 +81,6 @@ def test_get_index_file_valid_line():
         organization="Internet Widgits Pty Ltd "
     ), expiration_date)
 
-    expected = "V\t230401150111Z\t\t01\tunknown\t/CN=IA CA/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd "
+    expected = "V\t230401150111Z\t\t00000001\tunknown\t/CN=IA CA/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd "
 
     assert expected == index_line
