commit 2cecaf7060e6f4120dd5678030facc076525750a
Author: Jan Pašek <janpasek97@gmail.com>
Date:   Wed Apr 14 19:04:32 2021 +0200

    Re #8571 - Added RestAPI endpoint for revoking certificates

diff --git a/app.py b/app.py
index 8aab8e4..b5aeeb0 100644
--- a/app.py
+++ b/app.py
@@ -32,6 +32,11 @@ def get_cert(id, certificate_controller: CertController):
     return certificate_controller.get_certificate_by_id(id)
 
 
+@app.route('/api/certificates/<id>', methods=["PATCH"])
+def set_certificate_status(id, certificate_controller: CertController):
+    return certificate_controller.set_certificate_status(id)
+
+
 @app.route('/api/certificates/<id>/details', methods=["GET"])
 def get_cert_details(id, certificate_controller: CertController):
     return certificate_controller.get_certificate_details_by_id(id)
diff --git a/src/controllers/certificates_controller.py b/src/controllers/certificates_controller.py
index 6821e23..023f712 100644
--- a/src/controllers/certificates_controller.py
+++ b/src/controllers/certificates_controller.py
@@ -9,8 +9,10 @@ from injector import inject
 from src.constants import CA_ID, \
     SSL_ID, SIGNATURE_ID, AUTHENTICATION_ID, \
     DATETIME_FORMAT, ROOT_CA_ID, INTERMEDIATE_CA_ID, CERTIFICATE_ID  # TODO DATABASE_FILE - not the Controller's
+from src.exceptions.database_exception import DatabaseException
 from src.model.subject import Subject
-from src.services.certificate_service import CertificateService
+from src.services.certificate_service import CertificateService, RevocationReasonInvalidException, \
+    CertificateStatusInvalidException
 #  responsibility.
 from src.services.key_service import KeyService
 
@@ -27,6 +29,9 @@ USAGE = "usage"
 SUBJECT = "subject"
 VALIDITY_DAYS = "validityDays"
 CA = "CA"
+STATUS = "status"
+REASON = "reason"
+REASON_UNDEFINED = "undefined"
 
 E_NO_ISSUER_FOUND = {"success": False, "data": "No certificate authority with such unique ID exists."}
 E_NO_CERTIFICATES_FOUND = {"success": False, "data": "No such certificate found."}
@@ -41,7 +46,7 @@ E_WRONG_PARAMETERS = {"success": False, "data": "Invalid request, wrong paramete
 C_CREATED_SUCCESSFULLY = 201
 C_BAD_REQUEST = 400
 C_NOT_FOUND = 404
-C_NO_DATA = 205  # TODO related to 204 issue
+C_NO_DATA = 205  # TODO related to 204 issue                                               # TODO related to 204 issue
 C_INTERNAL_SERVER_ERROR = 500
 C_SUCCESS = 200
 
@@ -303,6 +308,49 @@ class CertController:
 
         return {"success": True, "data": "".join(ret)}, C_SUCCESS
 
+    def set_certificate_status(self, id):
+        """
+        Revoke a certificate given by ID
+            - revocation request may contain revocation reason
+            - revocation reason is verified based on the possible predefined values
+            - if revocation reason is not specified 'undefined' value is used
+        :param id: Identifier of the certificate to be revoked
+        :type id: int
+
+        :rtype: SuccessResponse | ErrorResponse (see OpenAPI definition)
+        """
+        required_keys = {STATUS}  # required keys
+
+        # try to parse certificate identifier -> if it is not int return error 400
+        try:
+            identifier = int(id)
+        except ValueError:
+            return E_WRONG_PARAMETERS, C_BAD_REQUEST
+
+        # check if the request contains a JSON body
+        if request.is_json:
+            request_body = request.get_json()
+            # verify that all required keys are present
+            if not all(k in request_body for k in required_keys):
+                return E_MISSING_PARAMETERS, C_BAD_REQUEST
+
+            # get status and reason from the request
+            status = request_body[STATUS]
+            reason = request_body.get(REASON, REASON_UNDEFINED)
+            try:
+                # set certificate status using certificate_service
+                self.certificate_service.set_certificate_revocation_status(status, reason)
+            except (RevocationReasonInvalidException, CertificateStatusInvalidException):
+                # these exceptions are thrown in case invalid status or revocation reason is passed to the controller
+                return E_WRONG_PARAMETERS, C_BAD_REQUEST
+            except DatabaseException:
+                return E_WRONG_PARAMETERS, C_BAD_REQUEST
+            return {"success": True,
+                    "data": "Certificate status updated successfully."}, C_CREATED_SUCCESSFULLY
+        # throw an error in case the request does not contain a json body
+        else:
+            return E_NOT_JSON_FORMAT, C_BAD_REQUEST
+
     def cert_to_dict_partial(self, c):
         """
         Dictionarizes a certificate directly fetched from the database. Contains partial information.
