Add SSL Certificates page

Adds ability to view, add, replace, and delete SSL
certificates in GUI.

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I5cf9fa7bbd588dfb22f2431eed0b5976ff860703
diff --git a/src/store/api.js b/src/store/api.js
index 8fdbdd2..24a38e4 100644
--- a/src/store/api.js
+++ b/src/store/api.js
@@ -29,8 +29,8 @@
   delete(path, payload) {
     return api.delete(path, payload);
   },
-  post(path, payload) {
-    return api.post(path, payload);
+  post(path, payload, config) {
+    return api.post(path, payload, config);
   },
   patch(path, payload) {
     return api.patch(path, payload);
diff --git a/src/store/index.js b/src/store/index.js
index 08ada05..0180213 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -4,6 +4,7 @@
 import GlobalStore from './modules/GlobalStore';
 import AuthenticationStore from './modules/Authentication/AuthenticanStore';
 import LocalUserManagementStore from './modules/AccessControl/LocalUserMangementStore';
+import SslCertificatesStore from './modules/AccessControl/SslCertificatesStore';
 import OverviewStore from './modules/Overview/OverviewStore';
 import FirmwareStore from './modules/Configuration/FirmwareStore';
 import BootSettingsStore from './modules/Control/BootSettingsStore';
@@ -32,7 +33,8 @@
     powerControl: PowerControlStore,
     networkSettings: NetworkSettingStore,
     eventLog: EventLogStore,
-    sensors: SensorsStore
+    sensors: SensorsStore,
+    sslCertificates: SslCertificatesStore
   },
   plugins: [WebSocketPlugin]
 });
diff --git a/src/store/modules/AccessControl/SslCertificatesStore.js b/src/store/modules/AccessControl/SslCertificatesStore.js
new file mode 100644
index 0000000..e1758d3
--- /dev/null
+++ b/src/store/modules/AccessControl/SslCertificatesStore.js
@@ -0,0 +1,158 @@
+import api from '../../api';
+import i18n from '../../../i18n';
+
+const CERTIFICATE_TYPES = [
+  {
+    type: 'HTTPS Certificate',
+    location: '/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/',
+    label: i18n.t('pageSslCertificates.httpsCertificate')
+  },
+  {
+    type: 'LDAP Certificate',
+    location: '/redfish/v1/AccountService/LDAP/Certificates/',
+    label: i18n.t('pageSslCertificates.ldapCertificate')
+  },
+  {
+    type: 'TrustStore Certificate',
+    location: '/redfish/v1/Managers/bmc/Truststore/Certificates/',
+    // Web UI will show 'CA Certificate' instead of
+    // 'TrustStore Certificate' after user testing revealed
+    // the term 'TrustStore Certificate' wasn't recognized/was unfamilar
+    label: i18n.t('pageSslCertificates.caCertificate')
+  }
+];
+
+const getCertificateProp = (type, prop) => {
+  const certificate = CERTIFICATE_TYPES.find(
+    certificate => certificate.type === type
+  );
+  return certificate ? certificate[prop] : null;
+};
+
+const SslCertificatesStore = {
+  namespaced: true,
+  state: {
+    allCertificates: [],
+    availableUploadTypes: []
+  },
+  getters: {
+    allCertificates: state => state.allCertificates,
+    availableUploadTypes: state => state.availableUploadTypes
+  },
+  mutations: {
+    setCertificates(state, certificates) {
+      state.allCertificates = certificates;
+    },
+    setAvailableUploadTypes(state, availableUploadTypes) {
+      state.availableUploadTypes = availableUploadTypes;
+    }
+  },
+  actions: {
+    getCertificates({ commit }) {
+      api
+        .get('/redfish/v1/CertificateService/CertificateLocations')
+        .then(({ data: { Links: { Certificates } } }) =>
+          Certificates.map(certificate => certificate['@odata.id'])
+        )
+        .then(certificateLocations => {
+          const promises = certificateLocations.map(location =>
+            api.get(location)
+          );
+          api.all(promises).then(
+            api.spread((...responses) => {
+              const certificates = responses.map(({ data }) => {
+                const {
+                  Name,
+                  ValidNotAfter,
+                  ValidNotBefore,
+                  Issuer = {},
+                  Subject = {}
+                } = data;
+                return {
+                  type: Name,
+                  location: data['@odata.id'],
+                  certificate: getCertificateProp(Name, 'label'),
+                  issuedBy: Issuer.CommonName,
+                  issuedTo: Subject.CommonName,
+                  validFrom: new Date(ValidNotBefore),
+                  validUntil: new Date(ValidNotAfter)
+                };
+              });
+              const availableUploadTypes = CERTIFICATE_TYPES.filter(
+                ({ type }) =>
+                  !certificates
+                    .map(certificate => certificate.type)
+                    .includes(type)
+              );
+
+              commit('setCertificates', certificates);
+              commit('setAvailableUploadTypes', availableUploadTypes);
+            })
+          );
+        });
+    },
+    async addNewCertificate({ dispatch }, { file, type }) {
+      return await api
+        .post(getCertificateProp(type, 'location'), file, {
+          headers: { 'Content-Type': 'application/x-pem-file' }
+        })
+        .then(() => dispatch('getCertificates'))
+        .then(() =>
+          i18n.t('pageSslCertificates.toast.successAddCertificate', {
+            certificate: getCertificateProp(type, 'label')
+          })
+        )
+        .catch(error => {
+          console.log(error);
+          throw new Error(
+            i18n.t('pageSslCertificates.toast.errorAddCertificate')
+          );
+        });
+    },
+    async replaceCertificate(
+      { dispatch },
+      { certificateString, location, type }
+    ) {
+      const data = {};
+      data.CertificateString = certificateString;
+      data.CertificateType = 'PEM';
+      data.CertificateUri = { '@odata.id': location };
+
+      return await api
+        .post(
+          '/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate',
+          data
+        )
+        .then(() => dispatch('getCertificates'))
+        .then(() =>
+          i18n.t('pageSslCertificates.toast.successReplaceCertificate', {
+            certificate: getCertificateProp(type, 'label')
+          })
+        )
+        .catch(error => {
+          console.log(error);
+          throw new Error(
+            i18n.t('pageSslCertificates.toast.errorReplaceCertificate')
+          );
+        });
+    },
+    async deleteCertificate({ dispatch }, { type, location }) {
+      return await api
+        .delete(location)
+        .then(() => dispatch('getCertificates'))
+        .then(() =>
+          i18n.t('pageSslCertificates.toast.successDeleteCertificate', {
+            certificate: getCertificateProp(type, 'label')
+          })
+        )
+        .catch(error => {
+          console.log(error);
+          throw new Error(
+            i18n.t('pageSslCertificates.toast.errorDeleteCertificate')
+          );
+        });
+    }
+  }
+};
+
+export default SslCertificatesStore;