Certificate delete API – frontend

With introducing option to add multiple certificates there is a need to give
user a possibility to remove selected certificates, for example when they
expire.
This commit adds implementation of DELETE function to GUI.
A new icon will appear in action section on certificate table.
The delete icon will be enabled only for TrustStore certificates and disabled
for others which does not have support for delete option.
When user clicks on the delete icon then ‘user prompt’ is displayed and after
confirmation, proper redfish action is used to delete the certificate.

Middlewere implementation is here:
 https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/25281

Backend implementation is here:
 https://gerrit.openbmc-project.xyz/c/openbmc/phosphor-certificate-manager/+/25268

Tested on Chrome and Mozilla.
    New icon appears in action section.
    The delete option is available only for TrustStore certificates.
    User is able to delete selected certificate.

Depends-On: I9781c5c79288ec5d080e80e42c63a55e471ddb77
Signed-off-by: Zbigniew Kurzynski <zbigniew.kurzynski@intel.com>
Change-Id: I68c5f54767d6982ae3cb00830b3a1b4f5e237bea
diff --git a/app/access-control/controllers/certificate-controller.html b/app/access-control/controllers/certificate-controller.html
index d2db65c..2166676 100644
--- a/app/access-control/controllers/certificate-controller.html
+++ b/app/access-control/controllers/certificate-controller.html
@@ -40,7 +40,7 @@
   <!-- TODO: Replace table with resusable table component -->
     <div class="section-content certificate__table">
       <div class="table__row-header">
-        <div class="row column">
+        <div class="row">
           <div class="certificate__type-header">
             Certificate
           </div>
@@ -57,6 +57,9 @@
           <div class="certificate__date-header">
             Valid until
           </div>
+          <div class="certificate__actions-header">
+            Actions
+          </div>
         </div>
       </div>
       <div ng-if="certificates.length < 1" class="empty__logs">
diff --git a/app/access-control/styles/certificate.scss b/app/access-control/styles/certificate.scss
index 2d882f4..cf3ab37 100644
--- a/app/access-control/styles/certificate.scss
+++ b/app/access-control/styles/certificate.scss
@@ -15,7 +15,7 @@
   }
   .certificate__type-header {
     @include mediaQuery(small) {
-      width: 20%;
+      width: 18%;
       background: transparent;
     }
     width: 100%;
@@ -26,14 +26,14 @@
     display: none;
     padding: 0.8em;
     @include mediaQuery(small) {
-      width: 20%;
+      width: 18%;
       display: block;
     }
   }
   .certificate__date-header {
     padding: 0.8em;
     @include mediaQuery(small) {
-      width: 12%;
+      width: 11%;
       display: block;
     }
     display: none;
@@ -46,13 +46,22 @@
     }
     display: none;
   }
+  .certificate__actions-header {
+    padding: 0.8em;
+    @include mediaQuery(small) {
+      width: auto;
+      padding-left: 30px;
+      display: block;
+    }
+    display: none;
+  }
   .certificate__type-cell {
     width: 100%;
     padding: 0.8em 0.8em 0.8em 1.5em;
     word-wrap: break-word;
     background: $background-02;
     @include mediaQuery(small) {
-      width: 20%;
+      width: 18%;
       background: transparent;
     }
   }
@@ -60,7 +69,7 @@
     padding: 0.8em;
     word-wrap: break-word;
     @include mediaQuery(small) {
-      width: 20%;
+      width: 18%;
     }
     width: 70%;
   }
@@ -69,7 +78,7 @@
     padding: 0.8em;
     word-wrap: break-word;
     @include mediaQuery(small) {
-      width: 12%;
+      width: 11%;
     }
   }
   .certificate__status-cell {
@@ -88,12 +97,19 @@
   }
   .certificate__buttons-cell {
     @include mediaQuery(small) {
-      width: 10%;
-      padding-top: 0.5em;
+      padding: 0.2em 0 0 2em;
+      .btn {
+        padding-left: 40px;
+        padding-right: 5px;
+      }
     }
-    width: 100%;
+    display:  block;
     text-align: right;
-    padding: 0 1.5em 0 0;
+    padding: 0.3em 0.8em 0.8em 1.0em;
+    .btn {
+      padding-left: 5px;
+      padding-right: 5px;
+    }
   }
   .certificate__title-inline {
     @include mediaQuery(small) {
diff --git a/app/assets/icons/icon-replace.svg b/app/assets/icons/icon-replace.svg
index edfd5f4..7f1d65c 100644
--- a/app/assets/icons/icon-replace.svg
+++ b/app/assets/icons/icon-replace.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg"><path fill-rule="nonzero" d="M5.452 9.452l-1.529.898-.54-.947L6.53 7.516l1.937 3.151-.87.562-1.07-1.7a3.323 3.323 0 0 0 3.689 4.997l.636 1.018a4.395 4.395 0 0 1-5.4-6.091zm8.224 2.945l1.467-.871.555 1.004-3.146 1.886-1.937-3.15.98-.624.938 1.545a3.328 3.328 0 0 0-2.626-4.136L9.25 7.002 9.395 7a4.395 4.395 0 0 1 4.281 5.397zM7 1H1.455C1.204 1 1 1.265 1 1.59v11.82c0 .325.204.59.455.59h3.263c.211.363.464.698.751 1H1.385C.62 15 0 14.225 0 13.27V1.73C0 .776.62 0 1.385 0h6c.122 0 .24.06.326.169l4.154 4.399a.657.657 0 0 1 .135.407v1.743a5.299 5.299 0 0 0-1-.45V5.011H7.727c-.401 0-.727-.556-.727-1.01V1zm1 .863v2.139h2.045L8 1.862z"/></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg"><path fill-rule="nonzero" transform="translate(7,3)" d="M5.452 9.452l-1.529.898-.54-.947L6.53 7.516l1.937 3.151-.87.562-1.07-1.7a3.323 3.323 0 0 0 3.689 4.997l.636 1.018a4.395 4.395 0 0 1-5.4-6.091zm8.224 2.945l1.467-.871.555 1.004-3.146 1.886-1.937-3.15.98-.624.938 1.545a3.328 3.328 0 0 0-2.626-4.136L9.25 7.002 9.395 7a4.395 4.395 0 0 1 4.281 5.397zM7 1H1.455C1.204 1 1 1.265 1 1.59v11.82c0 .325.204.59.455.59h3.263c.211.363.464.698.751 1H1.385C.62 15 0 14.225 0 13.27V1.73C0 .776.62 0 1.385 0h6c.122 0 .24.06.326.169l4.154 4.399a.657.657 0 0 1 .135.407v1.743a5.299 5.299 0 0 0-1-.45V5.011H7.727c-.401 0-.727-.556-.727-1.01V1zm1 .863v2.139h2.045L8 1.862z"/></svg>
\ No newline at end of file
diff --git a/app/common/directives/certificate-modal-remove.html b/app/common/directives/certificate-modal-remove.html
new file mode 100644
index 0000000..8de06f9
--- /dev/null
+++ b/app/common/directives/certificate-modal-remove.html
@@ -0,0 +1,21 @@
+<div class="uib-modal__content modal__local-certificates-remove">
+  <div class="modal-header">
+    <h2 class="modal-title" id="dialog_label">
+      Remove certificate
+    </h2>
+    <button type="button" class="btn  btn--close  float-right" ng-click="$dismiss()" aria-label="Close">
+      <icon file="icon-close.svg" aria-hidden="true"></icon>
+    </button>
+  </div>
+  <div class="modal-body">
+    <p>Are you sure you want to remove certificate issued by '{{modalCtrl.certificate.Issuer.CommonName}}' certificate? This action cannot be undone.</p>
+  </div>
+  <div class="modal-footer">
+    <button class="btn btn-secondary" ng-click="$dismiss()" type="button">
+      Cancel
+    </button>
+    <button class="btn btn-primary" ng-click="$close()" type="button">
+      Remove
+    </button>
+  </div>
+</div>
diff --git a/app/common/directives/certificate.html b/app/common/directives/certificate.html
index cf7b46d..86d0dc7 100644
--- a/app/common/directives/certificate.html
+++ b/app/common/directives/certificate.html
@@ -39,7 +39,10 @@
   <div class="certificate__date-cell">
     {{cert.ValidNotAfter | date:medium}}
   </div>
-  <div class="certificate__buttons-cell">
+  <div class="certificate__title-inline">
+    Actions:
+  </div>
+  <div class="certificate__buttons-cell row">
     <button
       type="button"
       ng-click="cert.upload = true"
@@ -47,6 +50,13 @@
       class="btn  btn-tertiary certificate__button">
       <icon file="icon-replace.svg" aria-hidden="true"></icon>
     </button>
+    <button type="button"
+      ng-click="confirmDeleteCert(cert)"
+      aria-label="Delete certificate"
+      class="btn  btn-tertiary certificate__button"
+      ng-disabled="!isDeletable(cert)">
+      <icon file="icon-trashcan.svg" aria-hidden="true"></icon>
+    </button>
   </div>
   <div ng-show="cert.upload === true" class="upload__certificate">
     <div class="close-btn">
diff --git a/app/common/directives/certificate.js b/app/common/directives/certificate.js
index 45b8c99..48fa851 100644
--- a/app/common/directives/certificate.js
+++ b/app/common/directives/certificate.js
@@ -9,8 +9,8 @@
         'template': require('./certificate.html'),
         'scope': {'cert': '=', 'reload': '&'},
         'controller': [
-          '$scope', 'APIUtils', 'toastService', 'Constants',
-          function($scope, APIUtils, toastService, Constants) {
+          '$scope', 'APIUtils', 'toastService', 'Constants', '$uibModal',
+          function($scope, APIUtils, toastService, Constants, $uibModal) {
             var certificateType = 'PEM';
             var availableCertificateTypes = Constants.CERTIFICATE_TYPES;
 
@@ -35,6 +35,65 @@
               }
             };
 
+            $scope.isDeletable = function(certificate) {
+              return certificate.Description == 'TrustStore Certificate';
+            };
+
+            $scope.confirmDeleteCert = function(certificate) {
+              initRemoveModal(certificate);
+            };
+
+            /**
+             * Intiate remove certificate modal
+             * @param {*} certificate
+             */
+            function initRemoveModal(certificate) {
+              const template = require('./certificate-modal-remove.html');
+              $uibModal
+                  .open({
+                    template,
+                    windowTopClass: 'uib-modal',
+                    ariaLabelledBy: 'dialog_label',
+                    controllerAs: 'modalCtrl',
+                    controller: function() {
+                      this.certificate = certificate;
+                    }
+                  })
+                  .result
+                  .then(() => {
+                    deleteCert(certificate);
+                  })
+                  .catch(
+                      () => {
+                          // do nothing
+                      })
+            };
+
+            /**
+             * Removes certificate
+             * @param {*} certificate
+             */
+            function deleteCert(certificate) {
+              $scope.confirm_delete = false;
+              APIUtils.deleteRedfishObject(certificate['@odata.id'])
+                  .then(
+                      function(data) {
+                        $scope.loading = false;
+                        toastService.success(
+                            $scope.getCertificateName(certificate.Description) +
+                            ' was deleted.');
+                        $scope.reload();
+                      },
+                      function(error) {
+                        console.log(error);
+                        $scope.loading = false;
+                        toastService.error(
+                            'Unable to delete ' +
+                            $scope.getCertificateName(certificate.Description));
+                      });
+              return;
+            };
+
             $scope.replaceCertificate = function(certificate) {
               $scope.loading = true;
               if (certificate.file.name.split('.').pop() !==
diff --git a/app/common/services/api-utils.js b/app/common/services/api-utils.js
index e3f6136..4298884 100644
--- a/app/common/services/api-utils.js
+++ b/app/common/services/api-utils.js
@@ -1432,6 +1432,16 @@
                 return response.data;
               });
         },
+        deleteRedfishObject: function(objectPath) {
+          return $http({
+                   method: 'DELETE',
+                   url: DataService.getHost() + objectPath,
+                   withCredentials: true
+                 })
+              .then(function(response) {
+                return response.data;
+              });
+        },
         getHardwares: function(callback) {
           $http({
             method: 'GET',