summaryrefslogtreecommitdiff
path: root/src/views/_sila/SecurityAndAccess/Certificates
diff options
context:
space:
mode:
Diffstat (limited to 'src/views/_sila/SecurityAndAccess/Certificates')
-rw-r--r--src/views/_sila/SecurityAndAccess/Certificates/Certificates.vue322
-rw-r--r--src/views/_sila/SecurityAndAccess/Certificates/CsrCountryCodes.js345
-rw-r--r--src/views/_sila/SecurityAndAccess/Certificates/ModalGenerateCsr.vue496
-rw-r--r--src/views/_sila/SecurityAndAccess/Certificates/ModalUploadCertificate.vue168
-rw-r--r--src/views/_sila/SecurityAndAccess/Certificates/index.js2
5 files changed, 1333 insertions, 0 deletions
diff --git a/src/views/_sila/SecurityAndAccess/Certificates/Certificates.vue b/src/views/_sila/SecurityAndAccess/Certificates/Certificates.vue
new file mode 100644
index 00000000..0113b80a
--- /dev/null
+++ b/src/views/_sila/SecurityAndAccess/Certificates/Certificates.vue
@@ -0,0 +1,322 @@
+<template>
+ <b-container fluid="xl">
+ <page-title />
+ <b-row>
+ <b-col xl="11">
+ <!-- Expired certificates banner -->
+ <alert :show="expiredCertificateTypes.length > 0" variant="danger">
+ <template v-if="expiredCertificateTypes.length > 1">
+ {{ $t('pageCertificates.alert.certificatesExpiredMessage') }}
+ </template>
+ <template v-else>
+ {{
+ $t('pageCertificates.alert.certificateExpiredMessage', {
+ certificate: expiredCertificateTypes[0],
+ })
+ }}
+ </template>
+ </alert>
+ <!-- Expiring certificates banner -->
+ <alert :show="expiringCertificateTypes.length > 0" variant="warning">
+ <template v-if="expiringCertificateTypes.length > 1">
+ {{ $t('pageCertificates.alert.certificatesExpiringMessage') }}
+ </template>
+ <template v-else>
+ {{
+ $t('pageCertificates.alert.certificateExpiringMessage', {
+ certificate: expiringCertificateTypes[0],
+ })
+ }}
+ </template>
+ </alert>
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col xl="11" class="text-right">
+ <b-button
+ v-b-modal.generate-csr
+ data-test-id="certificates-button-generateCsr"
+ variant="link"
+ >
+ <icon-add />
+ {{ $t('pageCertificates.generateCsr') }}
+ </b-button>
+ <b-button
+ variant="primary"
+ :disabled="certificatesForUpload.length === 0"
+ @click="initModalUploadCertificate(null)"
+ >
+ <icon-add />
+ {{ $t('pageCertificates.addNewCertificate') }}
+ </b-button>
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col xl="11">
+ <b-table
+ responsive="md"
+ show-empty
+ hover
+ :busy="isBusy"
+ :fields="fields"
+ :items="tableItems"
+ :empty-text="$t('global.table.emptyMessage')"
+ >
+ <template #cell(validFrom)="{ value }">
+ {{ value | formatDate }}
+ </template>
+
+ <template #cell(validUntil)="{ value }">
+ <status-icon
+ v-if="getDaysUntilExpired(value) < 31"
+ :status="getIconStatus(value)"
+ />
+ {{ value | formatDate }}
+ </template>
+
+ <template #cell(actions)="{ value, item }">
+ <table-row-action
+ v-for="(action, index) in value"
+ :key="index"
+ :value="action.value"
+ :title="action.title"
+ :enabled="action.enabled"
+ @click-table-action="onTableRowAction($event, item)"
+ >
+ <template #icon>
+ <icon-replace v-if="action.value === 'replace'" />
+ <icon-trashcan v-if="action.value === 'delete'" />
+ </template>
+ </table-row-action>
+ </template>
+ </b-table>
+ </b-col>
+ </b-row>
+
+ <!-- Modals -->
+ <modal-upload-certificate :certificate="modalCertificate" @ok="onModalOk" />
+ <modal-generate-csr />
+ </b-container>
+</template>
+
+<script>
+import IconAdd from '@carbon/icons-vue/es/add--alt/20';
+import IconReplace from '@carbon/icons-vue/es/renew/20';
+import IconTrashcan from '@carbon/icons-vue/es/trash-can/20';
+
+import ModalGenerateCsr from './ModalGenerateCsr';
+import ModalUploadCertificate from './ModalUploadCertificate';
+import PageTitle from '@/components/Global/PageTitle';
+import TableRowAction from '@/components/Global/TableRowAction';
+import StatusIcon from '@/components/Global/StatusIcon';
+import Alert from '@/components/Global/Alert';
+
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import LoadingBarMixin from '@/components/Mixins/LoadingBarMixin';
+
+export default {
+ name: 'Certificates',
+ components: {
+ Alert,
+ IconAdd,
+ IconReplace,
+ IconTrashcan,
+ ModalGenerateCsr,
+ ModalUploadCertificate,
+ PageTitle,
+ StatusIcon,
+ TableRowAction,
+ },
+ mixins: [BVToastMixin, LoadingBarMixin],
+ beforeRouteLeave(to, from, next) {
+ this.hideLoader();
+ next();
+ },
+ data() {
+ return {
+ isBusy: true,
+ modalCertificate: null,
+ fields: [
+ {
+ key: 'certificate',
+ label: this.$t('pageCertificates.table.certificate'),
+ },
+ {
+ key: 'issuedBy',
+ label: this.$t('pageCertificates.table.issuedBy'),
+ },
+ {
+ key: 'issuedTo',
+ label: this.$t('pageCertificates.table.issuedTo'),
+ },
+ {
+ key: 'validFrom',
+ label: this.$t('pageCertificates.table.validFrom'),
+ },
+ {
+ key: 'validUntil',
+ label: this.$t('pageCertificates.table.validUntil'),
+ },
+ {
+ key: 'actions',
+ label: '',
+ tdClass: 'text-right text-nowrap',
+ },
+ ],
+ };
+ },
+ computed: {
+ certificates() {
+ return this.$store.getters['certificates/allCertificates'];
+ },
+ tableItems() {
+ return this.certificates.map((certificate) => {
+ return {
+ ...certificate,
+ actions: [
+ {
+ value: 'replace',
+ title: this.$t('pageCertificates.replaceCertificate'),
+ },
+ {
+ value: 'delete',
+ title: this.$t('pageCertificates.deleteCertificate'),
+ enabled:
+ certificate.type === 'TrustStore Certificate' ? true : false,
+ },
+ ],
+ };
+ });
+ },
+ certificatesForUpload() {
+ return this.$store.getters['certificates/availableUploadTypes'];
+ },
+ bmcTime() {
+ return this.$store.getters['global/bmcTime'];
+ },
+ expiredCertificateTypes() {
+ return this.certificates.reduce((acc, val) => {
+ const daysUntilExpired = this.getDaysUntilExpired(val.validUntil);
+ if (daysUntilExpired < 1) {
+ acc.push(val.certificate);
+ }
+ return acc;
+ }, []);
+ },
+ expiringCertificateTypes() {
+ return this.certificates.reduce((acc, val) => {
+ const daysUntilExpired = this.getDaysUntilExpired(val.validUntil);
+ if (daysUntilExpired < 31 && daysUntilExpired > 0) {
+ acc.push(val.certificate);
+ }
+ return acc;
+ }, []);
+ },
+ },
+ async created() {
+ this.startLoader();
+ await this.$store.dispatch('global/getBmcTime');
+ this.$store.dispatch('certificates/getCertificates').finally(() => {
+ this.endLoader();
+ this.isBusy = false;
+ });
+ },
+ methods: {
+ onTableRowAction(event, rowItem) {
+ switch (event) {
+ case 'replace':
+ this.initModalUploadCertificate(rowItem);
+ break;
+ case 'delete':
+ this.initModalDeleteCertificate(rowItem);
+ break;
+ default:
+ break;
+ }
+ },
+ initModalUploadCertificate(certificate = null) {
+ this.modalCertificate = certificate;
+ this.$bvModal.show('upload-certificate');
+ },
+ initModalDeleteCertificate(certificate) {
+ this.$bvModal
+ .msgBoxConfirm(
+ this.$t('pageCertificates.modal.deleteConfirmMessage', {
+ issuedBy: certificate.issuedBy,
+ certificate: certificate.certificate,
+ }),
+ {
+ title: this.$t('pageCertificates.deleteCertificate'),
+ okTitle: this.$t('global.action.delete'),
+ cancelTitle: this.$t('global.action.cancel'),
+ }
+ )
+ .then((deleteConfirmed) => {
+ if (deleteConfirmed) this.deleteCertificate(certificate);
+ });
+ },
+ onModalOk({ addNew, file, type, location }) {
+ if (addNew) {
+ // Upload a new certificate
+ this.addNewCertificate(file, type);
+ } else {
+ // Replace an existing certificate
+ this.replaceCertificate(file, type, location);
+ }
+ },
+ addNewCertificate(file, type) {
+ this.startLoader();
+ this.$store
+ .dispatch('certificates/addNewCertificate', { file, type })
+ .then((success) => this.successToast(success))
+ .catch(({ message }) => this.errorToast(message))
+ .finally(() => this.endLoader());
+ },
+ replaceCertificate(file, type, location) {
+ this.startLoader();
+ const reader = new FileReader();
+ reader.readAsBinaryString(file);
+ reader.onloadend = (event) => {
+ const certificateString = event.target.result;
+ this.$store
+ .dispatch('certificates/replaceCertificate', {
+ certificateString,
+ type,
+ location,
+ })
+ .then((success) => this.successToast(success))
+ .catch(({ message }) => this.errorToast(message))
+ .finally(() => this.endLoader());
+ };
+ },
+ deleteCertificate({ type, location }) {
+ this.startLoader();
+ this.$store
+ .dispatch('certificates/deleteCertificate', {
+ type,
+ location,
+ })
+ .then((success) => this.successToast(success))
+ .catch(({ message }) => this.errorToast(message))
+ .finally(() => this.endLoader());
+ },
+ getDaysUntilExpired(date) {
+ if (this.bmcTime) {
+ const validUntilMs = date.getTime();
+ const currentBmcTimeMs = this.bmcTime.getTime();
+ const oneDayInMs = 24 * 60 * 60 * 1000;
+ return Math.round((validUntilMs - currentBmcTimeMs) / oneDayInMs);
+ }
+ return new Date();
+ },
+ getIconStatus(date) {
+ const daysUntilExpired = this.getDaysUntilExpired(date);
+ if (daysUntilExpired < 1) {
+ return 'danger';
+ } else if (daysUntilExpired < 31) {
+ return 'warning';
+ }
+ },
+ },
+};
+</script>
diff --git a/src/views/_sila/SecurityAndAccess/Certificates/CsrCountryCodes.js b/src/views/_sila/SecurityAndAccess/Certificates/CsrCountryCodes.js
new file mode 100644
index 00000000..a2d70007
--- /dev/null
+++ b/src/views/_sila/SecurityAndAccess/Certificates/CsrCountryCodes.js
@@ -0,0 +1,345 @@
+import i18n from '@/i18n';
+
+export const COUNTRY_LIST = [
+ { name: 'Afghanistan', code: 'AF', label: i18n.t('countries.AF') },
+ { name: 'Albania', code: 'AL', label: i18n.t('countries.AL') },
+ { name: 'Algeria', code: 'DZ', label: i18n.t('countries.DZ') },
+ { name: 'American Samoa', code: 'AS', label: i18n.t('countries.AS') },
+ { name: 'Andorra', code: 'AD', label: i18n.t('countries.AD') },
+ { name: 'Angola', code: 'AO', label: i18n.t('countries.AO') },
+ { name: 'Anguilla', code: 'AI', label: i18n.t('countries.AI') },
+ { name: 'Antarctica', code: 'AQ', label: i18n.t('countries.AQ') },
+ { name: 'Antigua and Barbuda', code: 'AG', label: i18n.t('countries.AG') },
+ { name: 'Argentina', code: 'AR', label: i18n.t('countries.AR') },
+ { name: 'Armenia', code: 'AM', label: i18n.t('countries.AM') },
+ { name: 'Aruba', code: 'AW', label: i18n.t('countries.AW') },
+ { name: 'Australia', code: 'AU', label: i18n.t('countries.AU') },
+ { name: 'Austria', code: 'AT', label: i18n.t('countries.AT') },
+ { name: 'Azerbaijan', code: 'AZ', label: i18n.t('countries.AZ') },
+ { name: 'Bahamas, The', code: 'BS', label: i18n.t('countries.BS') },
+ { name: 'Bahrain', code: 'BH', label: i18n.t('countries.BH') },
+ { name: 'Bangladesh', code: 'BD', label: i18n.t('countries.BD') },
+ { name: 'Barbados', code: 'BB', label: i18n.t('countries.BB') },
+ { name: 'Belarus', code: 'BY', label: i18n.t('countries.BY') },
+ { name: 'Belgium', code: 'BE', label: i18n.t('countries.BE') },
+ { name: 'Belize', code: 'BZ', label: i18n.t('countries.BZ') },
+ { name: 'Benin', code: 'BJ', label: i18n.t('countries.BJ') },
+ { name: 'Bermuda', code: 'BM', label: i18n.t('countries.BM') },
+ { name: 'Bhutan', code: 'BT', label: i18n.t('countries.BT') },
+ { name: 'Bolivia', code: 'BO', label: i18n.t('countries.BO') },
+ {
+ name: 'Bonaire, Sint Eustatius and Saba',
+ code: 'BQ',
+ label: i18n.t('countries.BQ'),
+ },
+ {
+ name: 'Bosnia and Herzegovina ',
+ code: 'BA',
+ label: i18n.t('countries.BA'),
+ },
+ { name: 'Bostwana', code: 'BW', label: i18n.t('countries.BW') },
+ { name: 'Bouvet Island', code: 'BV', label: i18n.t('countries.BV') },
+ { name: 'Brazil', code: 'BR', label: i18n.t('countries.BR') },
+ {
+ name: 'British Indian Ocean Territory',
+ code: 'IO',
+ label: i18n.t('countries.IO'),
+ },
+ { name: 'Brunei Darussalam ', code: 'BN', label: i18n.t('countries.BN') },
+ { name: 'Bulgaria', code: 'BG', label: i18n.t('countries.BG') },
+ { name: 'Burkina Faso', code: 'BF', label: i18n.t('countries.BF') },
+ { name: 'Burundi', code: 'BI', label: i18n.t('countries.BI') },
+ { name: 'Cabo Verde', code: 'CV', label: i18n.t('countries.CV') },
+ { name: 'Cambodia', code: 'KH', label: i18n.t('countries.KH') },
+ { name: 'Cameroon', code: 'CM', label: i18n.t('countries.CM') },
+ { name: 'Canada', code: 'CA', label: i18n.t('countries.CA') },
+ { name: 'Cayman Islands', code: 'KY', label: i18n.t('countries.KY') },
+ {
+ name: 'Central African Republic',
+ code: 'CF',
+ label: i18n.t('countries.CF'),
+ },
+ { name: 'Chad', code: 'TD', label: i18n.t('countries.TD') },
+ { name: 'Chile', code: 'CL', label: i18n.t('countries.CL') },
+ { name: 'China', code: 'CN', label: i18n.t('countries.CN') },
+ { name: 'Christmas Island ', code: 'CX', label: i18n.t('countries.CX') },
+ { name: 'Cocos(Keeling) Islands', code: 'CC', label: i18n.t('countries.CC') },
+ { name: 'Columbia', code: 'CO', label: i18n.t('countries.CO') },
+ { name: 'Comoros', code: 'KM', label: i18n.t('countries.KM') },
+ {
+ name: 'Congo, The Democratic Republic of the',
+ code: 'CD',
+ label: i18n.t('countries.CD'),
+ },
+ { name: 'Congo', code: 'CG', label: i18n.t('countries.CG') },
+ { name: 'Cook Islands', code: 'CK', label: i18n.t('countries.CK') },
+ { name: 'Costa Rica', code: 'CR', label: i18n.t('countries.CR') },
+ { name: 'Croatia', code: 'HR', label: i18n.t('countries.HR') },
+ { name: 'Cuba', code: 'CU', label: i18n.t('countries.CU') },
+ { name: 'Curaçao', code: 'CW', label: i18n.t('countries.CW') },
+ { name: 'Cyprus', code: 'CY', label: i18n.t('countries.CY') },
+ { name: 'Czechia', code: 'CZ', label: i18n.t('countries.CZ') },
+ { name: "Côte d'Ivoire", code: 'CI', label: i18n.t('countries.CI') },
+ { name: 'Denmark', code: 'DK', label: i18n.t('countries.DK') },
+ { name: 'Djibouti', code: 'DJ', label: i18n.t('countries.DJ') },
+ { name: 'Dominica', code: 'DM', label: i18n.t('countries.DM') },
+ { name: 'Dominican Republic', code: 'DO', label: i18n.t('countries.DO') },
+ { name: 'Ecuador', code: 'EC', label: i18n.t('countries.EC') },
+ { name: 'Egypt', code: 'EG', label: i18n.t('countries.EG') },
+ { name: 'El Salvador', code: 'SV', label: i18n.t('countries.SV') },
+ { name: 'Equatorial Guinea ', code: 'GQ', label: i18n.t('countries.GQ') },
+ { name: 'Eritrea', code: 'ER', label: i18n.t('countries.ER') },
+ { name: 'Estonia', code: 'EE', label: i18n.t('countries.EE') },
+ { name: 'Eswatini', code: 'SZ', label: i18n.t('countries.SZ') },
+ { name: 'Ethiopia', code: 'ET', label: i18n.t('countries.ET') },
+ {
+ name: 'Falkland Islands (Malvinas)',
+ code: 'FK',
+ label: i18n.t('countries.FK'),
+ },
+ { name: 'Faroe Islands', code: 'FO', label: i18n.t('countries.FO') },
+ { name: 'Fiji', code: 'FJ', label: i18n.t('countries.FJ') },
+ { name: 'Finland', code: 'FI', label: i18n.t('countries.FI') },
+ { name: 'France', code: 'FR', label: i18n.t('countries.FR') },
+ { name: 'French Guiana', code: 'GF', label: i18n.t('countries.GF') },
+ { name: 'French Polynesia', code: 'PF', label: i18n.t('countries.PF') },
+ {
+ name: 'French Southern Territories',
+ code: 'TF',
+ label: i18n.t('countries.TF'),
+ },
+ { name: 'Gabon', code: 'GA', label: i18n.t('countries.GA') },
+ { name: 'Gambia, The', code: 'GM', label: i18n.t('countries.GM') },
+ { name: 'Georgia', code: 'GE', label: i18n.t('countries.GE') },
+ { name: 'Germany', code: 'DE', label: i18n.t('countries.DE') },
+ { name: 'Ghana', code: 'GH', label: i18n.t('countries.GH') },
+ { name: 'Gibraltar', code: 'GI', label: i18n.t('countries.GI') },
+ { name: 'Greece', code: 'GR', label: i18n.t('countries.GR') },
+ { name: 'Greenland', code: 'GL', label: i18n.t('countries.GL') },
+ { name: 'Grenada', code: 'GD', label: i18n.t('countries.GD') },
+ { name: 'Guadeloupe', code: 'GP', label: i18n.t('countries.GP') },
+ { name: 'Guam', code: 'GU', label: i18n.t('countries.GU') },
+ { name: 'Guatemala', code: 'GT', label: i18n.t('countries.GT') },
+ { name: 'Guernsey', code: 'GG', label: i18n.t('countries.GG') },
+ { name: 'Guinea', code: 'GN', label: i18n.t('countries.GN') },
+ { name: 'Guinea-Bissau', code: 'GW', label: i18n.t('countries.GW') },
+ { name: 'Guyana', code: 'GY', label: i18n.t('countries.GY') },
+ { name: 'Haiti', code: 'HT', label: i18n.t('countries.HT') },
+ {
+ name: 'Heard Island and McDonald Islands',
+ code: 'HM',
+ label: i18n.t('countries.HM'),
+ },
+ { name: 'Holy See', code: 'VA', label: i18n.t('countries.VA') },
+ { name: 'Honduras', code: 'HN', label: i18n.t('countries.HN') },
+ { name: 'Hong Kong', code: 'HK', label: i18n.t('countries.HK') },
+ { name: 'Hungary', code: 'HU', label: i18n.t('countries.HU') },
+ { name: 'Iceland', code: 'IS', label: i18n.t('countries.IS') },
+ { name: 'India', code: 'IN', label: i18n.t('countries.IN') },
+ { name: 'Indonesia', code: 'ID', label: i18n.t('countries.ID') },
+ {
+ name: 'Iran, Islamic Republic of',
+ code: 'IR',
+ label: i18n.t('countries.IR'),
+ },
+ { name: 'Iraq', code: 'IQ', label: i18n.t('countries.IQ') },
+ { name: 'Ireland', code: 'IE', label: i18n.t('countries.IE') },
+ { name: 'Isle of Man', code: 'IM', label: i18n.t('countries.IM') },
+ { name: 'Israel', code: 'IL', label: i18n.t('countries.IL') },
+ { name: 'Italy', code: 'IT', label: i18n.t('countries.IT') },
+ { name: 'Jamaica', code: 'JM', label: i18n.t('countries.JM') },
+ { name: 'Japan', code: 'JP', label: i18n.t('countries.JP') },
+ { name: 'Jersey', code: 'JE', label: i18n.t('countries.JE') },
+ { name: 'Jordan', code: 'JO', label: i18n.t('countries.JO') },
+ { name: 'Kazakhstan', code: 'KZ', label: i18n.t('countries.KZ') },
+ { name: 'Kenya', code: 'KE', label: i18n.t('countries.KE') },
+ { name: 'Kiribati', code: 'KI', label: i18n.t('countries.KI') },
+ { name: 'Korea, Republic of', code: 'KR', label: i18n.t('countries.KR') },
+ {
+ name: "Korea, Democratic People's Republic of",
+ code: 'KP',
+ label: i18n.t('countries.KP'),
+ },
+ { name: 'Kuwait', code: 'KW', label: i18n.t('countries.KW') },
+ { name: 'Kyrgyzstan', code: 'KG', label: i18n.t('countries.KG') },
+ {
+ name: "Lao People's Democratic Republic",
+ code: 'LA',
+ label: i18n.t('countries.LA'),
+ },
+ { name: 'Latvia', code: 'LV', label: i18n.t('countries.LV') },
+ { name: 'Lebanon', code: 'LB', label: i18n.t('countries.LB') },
+ { name: 'Lesotho', code: 'LS', label: i18n.t('countries.LS') },
+ { name: 'Liberia', code: 'LR', label: i18n.t('countries.LR') },
+ { name: 'Libya', code: 'LY', label: i18n.t('countries.LY') },
+ { name: 'Liechtenstein', code: 'LI', label: i18n.t('countries.LI') },
+ { name: 'Lithuania', code: 'LT', label: i18n.t('countries.LT') },
+ { name: 'Luxembourg', code: 'LU', label: i18n.t('countries.LU') },
+ { name: 'Macao', code: 'MO', label: i18n.t('countries.MO') },
+ {
+ name: 'Macedonia, The Former Yugoslav Republic of',
+ code: 'MK',
+ label: i18n.t('countries.MK'),
+ },
+ { name: 'Madagascar', code: 'MG', label: i18n.t('countries.MG') },
+ { name: 'Malawi', code: 'MW', label: i18n.t('countries.MW') },
+ { name: 'Malaysia', code: 'MY', label: i18n.t('countries.MY') },
+ { name: 'Maldives', code: 'MV', label: i18n.t('countries.MV') },
+ { name: 'Mali', code: 'ML', label: i18n.t('countries.ML') },
+ { name: 'Malta', code: 'MT', label: i18n.t('countries.MT') },
+ { name: 'Marshall Islands', code: 'MH', label: i18n.t('countries.MH') },
+ { name: 'Martinique', code: 'MQ', label: i18n.t('countries.MQ') },
+ { name: 'Mauritania', code: 'MR', label: i18n.t('countries.MR') },
+ { name: 'Mauritius', code: 'MU', label: i18n.t('countries.MU') },
+ { name: 'Mayotte', code: 'YT', label: i18n.t('countries.YT') },
+ { name: 'Mexico', code: 'MX', label: i18n.t('countries.MX') },
+ {
+ name: 'Micronesia, Federated States of',
+ code: 'FM',
+ label: i18n.t('countries.FM'),
+ },
+ { name: 'Moldova, Republic of', code: 'MD', label: i18n.t('countries.MD') },
+ { name: 'Monaco', code: 'MC', label: i18n.t('countries.MC') },
+ { name: 'Mongolia', code: 'MN', label: i18n.t('countries.MN') },
+ { name: 'Montenegro', code: 'ME', label: i18n.t('countries.ME') },
+ { name: 'Montserrat', code: 'MS', label: i18n.t('countries.MS') },
+ { name: 'Morocco', code: 'MA', label: i18n.t('countries.MA') },
+ { name: 'Mozambique', code: 'MZ', label: i18n.t('countries.MZ') },
+ { name: 'Myanmar', code: 'MM', label: i18n.t('countries.MM') },
+ { name: 'Namibia', code: 'NA', label: i18n.t('countries.NA') },
+ { name: 'Nauru', code: 'NR', label: i18n.t('countries.NR') },
+ { name: 'Nepal', code: 'NP', label: i18n.t('countries.NP') },
+ { name: 'Netherlands', code: 'NL', label: i18n.t('countries.NL') },
+ { name: 'New Caledonia', code: 'NC', label: i18n.t('countries.NC') },
+ { name: 'New Zealand', code: 'NZ', label: i18n.t('countries.NZ') },
+ { name: 'Nicaragua', code: 'NI', label: i18n.t('countries.NI') },
+ { name: 'Niger', code: 'NE', label: i18n.t('countries.NE') },
+ { name: 'Nigeria', code: 'NG', label: i18n.t('countries.NG') },
+ { name: 'Niue', code: 'NU', label: i18n.t('countries.NU') },
+ { name: 'Norfolk Island', code: 'NF', label: i18n.t('countries.NF') },
+ {
+ name: 'Northern Mariana Islands',
+ code: 'MP',
+ label: i18n.t('countries.MP'),
+ },
+ { name: 'Norway', code: 'NO', label: i18n.t('countries.NO') },
+ { name: 'Oman', code: 'OM', label: i18n.t('countries.OM') },
+ { name: 'Pakistan', code: 'PK', label: i18n.t('countries.PK') },
+ { name: 'Palau', code: 'PW', label: i18n.t('countries.PW') },
+ { name: 'Palestine', code: 'PS', label: i18n.t('countries.PS') },
+ { name: 'Panama', code: 'PA', label: i18n.t('countries.PA') },
+ { name: 'Papua New Guinea', code: 'PG', label: i18n.t('countries.PG') },
+ { name: 'Paraguay', code: 'PY', label: i18n.t('countries.PY') },
+ { name: 'Peru', code: 'PE', label: i18n.t('countries.PE') },
+ { name: 'Philippines', code: 'PH', label: i18n.t('countries.PH') },
+ { name: 'Pitcairn', code: 'PN', label: i18n.t('countries.PN') },
+ { name: 'Poland', code: 'PL', label: i18n.t('countries.PL') },
+ { name: 'Portugal', code: 'PT', label: i18n.t('countries.PT') },
+ { name: 'Puerto Rico', code: 'PR', label: i18n.t('countries.PR') },
+ { name: 'Qatar', code: 'QA', label: i18n.t('countries.QA') },
+ { name: 'Romania', code: 'RO', label: i18n.t('countries.RO') },
+ { name: 'Russian Federation', code: 'RU', label: i18n.t('countries.RU') },
+ { name: 'Rwanda', code: 'RW', label: i18n.t('countries.RW') },
+ { name: 'Réunion', code: 'RE', label: i18n.t('countries.RE') },
+ { name: 'Saint Barthélemy', code: 'BL', label: i18n.t('countries.BL') },
+ {
+ name: 'Saint Helena, Ascension and Tristan da Cunha',
+ code: 'SH',
+ label: i18n.t('countries.SH'),
+ },
+ { name: 'Saint Kitts and Nevis ', code: 'KN', label: i18n.t('countries.KN') },
+ { name: 'Saint Lucia', code: 'LC', label: i18n.t('countries.LC') },
+ { name: 'Saint Martin', code: 'MF', label: i18n.t('countries.MF') },
+ {
+ name: 'Saint Pierre and Miquelon',
+ code: 'PM',
+ label: i18n.t('countries.PM'),
+ },
+ {
+ name: 'Saint Vincent and the Grenadines',
+ code: 'VC',
+ label: i18n.t('countries.VC'),
+ },
+ { name: 'Samoa', code: 'WS', label: i18n.t('countries.WS') },
+ { name: 'San Marino ', code: 'SM', label: i18n.t('countries.SM') },
+ { name: 'Sao Tome and Principe', code: 'ST', label: i18n.t('countries.ST') },
+ { name: 'Saudi Arabia', code: 'SA', label: i18n.t('countries.SA') },
+ { name: 'Senegal', code: 'SN', label: i18n.t('countries.SN') },
+ { name: 'Serbia', code: 'RS', label: i18n.t('countries.RS') },
+ { name: 'Seychelles', code: 'SC', label: i18n.t('countries.SC') },
+ { name: 'Sierra Leone', code: 'SL', label: i18n.t('countries.SL') },
+ { name: 'Singapore', code: 'SG', label: i18n.t('countries.SG') },
+ { name: 'Sint Maarten', code: 'SX', label: i18n.t('countries.SX') },
+ { name: 'Slovakia', code: 'SK', label: i18n.t('countries.SK') },
+ { name: 'Slovenia', code: 'SI', label: i18n.t('countries.SI') },
+ { name: 'Solomon Islands', code: 'SB', label: i18n.t('countries.SB') },
+ { name: 'Somalia', code: 'SO', label: i18n.t('countries.SO') },
+ { name: 'South Africa ', code: 'ZA', label: i18n.t('countries.ZA') },
+ {
+ name: 'South Georgia and the South Sandwich Islands',
+ code: 'GS',
+ label: i18n.t('countries.GS'),
+ },
+ { name: 'South Sudan', code: 'SS', label: i18n.t('countries.SS') },
+ { name: 'Spain', code: 'ES', label: i18n.t('countries.ES') },
+ { name: 'Sri Lanka', code: 'LK', label: i18n.t('countries.LK') },
+ { name: 'Sudan', code: 'SD', label: i18n.t('countries.SD') },
+ { name: 'Suriname', code: 'SR', label: i18n.t('countries.SR') },
+ { name: 'Svalbard and Jan Mayen', code: 'SJ', label: i18n.t('countries.SJ') },
+ { name: 'Sweden', code: 'SE', label: i18n.t('countries.SE') },
+ { name: 'Switzerland', code: 'CH', label: i18n.t('countries.CH') },
+ { name: 'Syrian Arab Republic', code: 'SY', label: i18n.t('countries.SY') },
+ { name: 'Taiwan', code: 'TW', label: i18n.t('countries.TW') },
+ { name: 'Tajikistan', code: 'TJ', label: i18n.t('countries.TJ') },
+ {
+ name: 'Tanzania, United Republic of',
+ code: 'TZ',
+ label: i18n.t('countries.TZ'),
+ },
+ { name: 'Thailand', code: 'TH', label: i18n.t('countries.TH') },
+ { name: 'Timor-Leste', code: 'TL', label: i18n.t('countries.TL') },
+ { name: 'Togo', code: 'TG', label: i18n.t('countries.TG') },
+ { name: 'Tokelau', code: 'TK', label: i18n.t('countries.TK') },
+ { name: 'Tonga', code: 'TO', label: i18n.t('countries.TO') },
+ { name: 'Trinidad and Tobago', code: 'TT', label: i18n.t('countries.TT') },
+ { name: 'Tunisia', code: 'TN', label: i18n.t('countries.TN') },
+ { name: 'Turkey', code: 'TR', label: i18n.t('countries.TR') },
+ { name: 'Turkmenistan', code: 'TM', label: i18n.t('countries.TM') },
+ {
+ name: 'Turks and Caicos Islands',
+ code: 'TC',
+ label: i18n.t('countries.TC'),
+ },
+ { name: 'Tuvalu', code: 'TV', label: i18n.t('countries.TV') },
+ { name: 'Uganda', code: 'UG', label: i18n.t('countries.UG') },
+ { name: 'Ukraine', code: 'UA', label: i18n.t('countries.UA') },
+ { name: 'United Arab Emirates', code: 'AE', label: i18n.t('countries.AE') },
+ { name: 'United Kingdom', code: 'GB', label: i18n.t('countries.GB') },
+ {
+ name: 'United States Minor Outlying Islands',
+ code: 'UM',
+ label: i18n.t('countries.UM'),
+ },
+ {
+ name: 'United States of America',
+ code: 'US',
+ label: i18n.t('countries.US'),
+ },
+ { name: 'Uruguay', code: 'UY', label: i18n.t('countries.UY') },
+ { name: 'Uzbekistan', code: 'UZ', label: i18n.t('countries.UZ') },
+ { name: 'Vanuatu', code: 'VU', label: i18n.t('countries.VU') },
+ { name: 'Venezuela', code: 'VE', label: i18n.t('countries.VE') },
+ { name: 'Viet Nam', code: 'VN', label: i18n.t('countries.VN') },
+ {
+ name: 'Virgin Islands, British',
+ code: 'VG',
+ label: i18n.t('countries.VG'),
+ },
+ { name: 'Virgin Islands, U.S', code: 'VI', label: i18n.t('countries.VI') },
+ { name: 'Wallis and Futuna', code: 'WF', label: i18n.t('countries.WF') },
+ { name: 'Western Sahara', code: 'EH', label: i18n.t('countries.EH') },
+ { name: 'Yemen', code: 'YE', label: i18n.t('countries.YE') },
+ { name: 'Zambia', code: 'ZM', label: i18n.t('countries.ZM') },
+ { name: 'Zimbabwe', code: 'ZW', label: i18n.t('countries.ZW') },
+ { name: 'Åland Islands', code: 'AX', label: i18n.t('countries.AX') },
+];
diff --git a/src/views/_sila/SecurityAndAccess/Certificates/ModalGenerateCsr.vue b/src/views/_sila/SecurityAndAccess/Certificates/ModalGenerateCsr.vue
new file mode 100644
index 00000000..d76f9fe1
--- /dev/null
+++ b/src/views/_sila/SecurityAndAccess/Certificates/ModalGenerateCsr.vue
@@ -0,0 +1,496 @@
+<template>
+ <div>
+ <b-modal
+ id="generate-csr"
+ ref="modal"
+ size="lg"
+ no-stacking
+ :title="$t('pageCertificates.modal.generateACertificateSigningRequest')"
+ @ok="onOkGenerateCsrModal"
+ @cancel="resetForm"
+ @hidden="$v.$reset()"
+ >
+ <b-form id="generate-csr-form" novalidate @submit.prevent="handleSubmit">
+ <b-container fluid>
+ <b-row>
+ <b-col lg="9">
+ <b-row>
+ <b-col lg="6">
+ <b-form-group
+ :label="$t('pageCertificates.modal.certificateType')"
+ label-for="certificate-type"
+ >
+ <b-form-select
+ id="certificate-type"
+ v-model="form.certificateType"
+ data-test-id="modalGenerateCsr-select-certificateType"
+ :options="certificateOptions"
+ :state="getValidationState($v.form.certificateType)"
+ @input="$v.form.certificateType.$touch()"
+ >
+ <template #first>
+ <b-form-select-option :value="null" disabled>
+ {{ $t('global.form.selectAnOption') }}
+ </b-form-select-option>
+ </template>
+ </b-form-select>
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ <b-col lg="6">
+ <b-form-group
+ :label="$t('pageCertificates.modal.country')"
+ label-for="country"
+ >
+ <b-form-select
+ id="country"
+ v-model="form.country"
+ data-test-id="modalGenerateCsr-select-country"
+ :options="countryOptions"
+ :state="getValidationState($v.form.country)"
+ @input="$v.form.country.$touch()"
+ >
+ <template #first>
+ <b-form-select-option :value="null" disabled>
+ {{ $t('global.form.selectAnOption') }}
+ </b-form-select-option>
+ </template>
+ </b-form-select>
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col lg="6">
+ <b-form-group
+ :label="$t('pageCertificates.modal.state')"
+ label-for="state"
+ >
+ <b-form-input
+ id="state"
+ v-model="form.state"
+ type="text"
+ data-test-id="modalGenerateCsr-input-state"
+ :state="getValidationState($v.form.state)"
+ />
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ <b-col lg="6">
+ <b-form-group
+ :label="$t('pageCertificates.modal.city')"
+ label-for="city"
+ >
+ <b-form-input
+ id="city"
+ v-model="form.city"
+ type="text"
+ data-test-id="modalGenerateCsr-input-city"
+ :state="getValidationState($v.form.city)"
+ />
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col lg="6">
+ <b-form-group
+ :label="$t('pageCertificates.modal.companyName')"
+ label-for="company-name"
+ >
+ <b-form-input
+ id="company-name"
+ v-model="form.companyName"
+ type="text"
+ data-test-id="modalGenerateCsr-input-companyName"
+ :state="getValidationState($v.form.companyName)"
+ />
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ <b-col lg="6">
+ <b-form-group
+ :label="$t('pageCertificates.modal.companyUnit')"
+ label-for="company-unit"
+ >
+ <b-form-input
+ id="company-unit"
+ v-model="form.companyUnit"
+ type="text"
+ data-test-id="modalGenerateCsr-input-companyUnit"
+ :state="getValidationState($v.form.companyUnit)"
+ />
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col lg="6">
+ <b-form-group
+ :label="$t('pageCertificates.modal.commonName')"
+ label-for="common-name"
+ >
+ <b-form-input
+ id="common-name"
+ v-model="form.commonName"
+ type="text"
+ data-test-id="modalGenerateCsr-input-commonName"
+ :state="getValidationState($v.form.commonName)"
+ />
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ <b-col lg="6">
+ <b-form-group label-for="challenge-password">
+ <template #label>
+ {{ $t('pageCertificates.modal.challengePassword') }} -
+ <span class="form-text d-inline">
+ {{ $t('global.form.optional') }}
+ </span>
+ </template>
+ <b-form-input
+ id="challenge-password"
+ v-model="form.challengePassword"
+ type="text"
+ data-test-id="modalGenerateCsr-input-challengePassword"
+ />
+ </b-form-group>
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col lg="6">
+ <b-form-group label-for="contact-person">
+ <template #label>
+ {{ $t('pageCertificates.modal.contactPerson') }} -
+ <span class="form-text d-inline">
+ {{ $t('global.form.optional') }}
+ </span>
+ </template>
+ <b-form-input
+ id="contact-person"
+ v-model="form.contactPerson"
+ type="text"
+ data-test-id="modalGenerateCsr-input-contactPerson"
+ />
+ </b-form-group>
+ </b-col>
+ <b-col lg="6">
+ <b-form-group label-for="email-address">
+ <template #label>
+ {{ $t('pageCertificates.modal.emailAddress') }} -
+ <span class="form-text d-inline">
+ {{ $t('global.form.optional') }}
+ </span>
+ </template>
+ <b-form-input
+ id="email-address"
+ v-model="form.emailAddress"
+ type="text"
+ data-test-id="modalGenerateCsr-input-emailAddress"
+ />
+ </b-form-group>
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col lg="12">
+ <b-form-group label-for="alternate-name">
+ <template #label>
+ {{ $t('pageCertificates.modal.alternateName') }} -
+ <span class="form-text d-inline">
+ {{ $t('global.form.optional') }}
+ </span>
+ </template>
+ <b-form-text id="alternate-name-help-block">
+ {{ $t('pageCertificates.modal.alternateNameHelperText') }}
+ </b-form-text>
+ <b-form-tags
+ v-model="form.alternateName"
+ :remove-on-delete="true"
+ :tag-pills="true"
+ input-id="alternate-name"
+ size="lg"
+ separator=" "
+ :input-attrs="{
+ 'aria-describedby': 'alternate-name-help-block',
+ }"
+ :duplicate-tag-text="
+ $t('pageCertificates.modal.duplicateAlternateName')
+ "
+ placeholder=""
+ data-test-id="modalGenerateCsr-input-alternateName"
+ >
+ <template #add-button-text>
+ <icon-add /> {{ $t('global.action.add') }}
+ </template>
+ </b-form-tags>
+ </b-form-group>
+ </b-col>
+ </b-row>
+ </b-col>
+ <b-col lg="3">
+ <b-row>
+ <b-col lg="12">
+ <p class="col-form-label">
+ {{ $t('pageCertificates.modal.privateKey') }}
+ </p>
+ <b-form-group
+ :label="$t('pageCertificates.modal.keyPairAlgorithm')"
+ label-for="key-pair-algorithm"
+ >
+ <b-form-select
+ id="key-pair-algorithm"
+ v-model="form.keyPairAlgorithm"
+ data-test-id="modalGenerateCsr-select-keyPairAlgorithm"
+ :options="keyPairAlgorithmOptions"
+ :state="getValidationState($v.form.keyPairAlgorithm)"
+ @input="$v.form.keyPairAlgorithm.$touch()"
+ >
+ <template #first>
+ <b-form-select-option :value="null" disabled>
+ {{ $t('global.form.selectAnOption') }}
+ </b-form-select-option>
+ </template>
+ </b-form-select>
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </b-col>
+ </b-row>
+ <b-row>
+ <b-col lg="12">
+ <template v-if="$v.form.keyPairAlgorithm.$model === 'EC'">
+ <b-form-group
+ :label="$t('pageCertificates.modal.keyCurveId')"
+ label-for="key-curve-id"
+ >
+ <b-form-select
+ id="key-curve-id"
+ v-model="form.keyCurveId"
+ data-test-id="modalGenerateCsr-select-keyCurveId"
+ :options="keyCurveIdOptions"
+ :state="getValidationState($v.form.keyCurveId)"
+ @input="$v.form.keyCurveId.$touch()"
+ >
+ <template #first>
+ <b-form-select-option :value="null" disabled>
+ {{ $t('global.form.selectAnOption') }}
+ </b-form-select-option>
+ </template>
+ </b-form-select>
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </template>
+ <template v-if="$v.form.keyPairAlgorithm.$model === 'RSA'">
+ <b-form-group
+ :label="$t('pageCertificates.modal.keyBitLength')"
+ label-for="key-bit-length"
+ >
+ <b-form-select
+ id="key-bit-length"
+ v-model="form.keyBitLength"
+ data-test-id="modalGenerateCsr-select-keyBitLength"
+ :options="keyBitLengthOptions"
+ :state="getValidationState($v.form.keyBitLength)"
+ @input="$v.form.keyBitLength.$touch()"
+ >
+ <template #first>
+ <b-form-select-option :value="null" disabled>
+ {{ $t('global.form.selectAnOption') }}
+ </b-form-select-option>
+ </template>
+ </b-form-select>
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.fieldRequired') }}
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </template>
+ </b-col>
+ </b-row>
+ </b-col>
+ </b-row>
+ </b-container>
+ </b-form>
+ <template #modal-footer="{ ok, cancel }">
+ <b-button variant="secondary" @click="cancel()">
+ {{ $t('global.action.cancel') }}
+ </b-button>
+ <b-button
+ form="generate-csr-form"
+ type="submit"
+ variant="primary"
+ data-test-id="modalGenerateCsr-button-ok"
+ @click="ok()"
+ >
+ {{ $t('pageCertificates.generateCsr') }}
+ </b-button>
+ </template>
+ </b-modal>
+ <b-modal
+ id="csr-string"
+ no-stacking
+ size="lg"
+ :title="$t('pageCertificates.modal.certificateSigningRequest')"
+ @hidden="onHiddenCsrStringModal"
+ >
+ {{ csrString }}
+ <template #modal-footer>
+ <b-btn variant="secondary" @click="copyCsrString">
+ <template v-if="csrStringCopied">
+ <icon-checkmark />
+ {{ $t('global.status.copied') }}
+ </template>
+ <template v-else>
+ {{ $t('global.action.copy') }}
+ </template>
+ </b-btn>
+ <a
+ :href="`data:text/json;charset=utf-8,${csrString}`"
+ download="certificate.txt"
+ class="btn btn-primary"
+ >
+ {{ $t('global.action.download') }}
+ </a>
+ </template>
+ </b-modal>
+ </div>
+</template>
+
+<script>
+import IconAdd from '@carbon/icons-vue/es/add--alt/20';
+import IconCheckmark from '@carbon/icons-vue/es/checkmark/20';
+
+import { required, requiredIf } from 'vuelidate/lib/validators';
+
+import { COUNTRY_LIST } from './CsrCountryCodes';
+import { CERTIFICATE_TYPES } from '@/store/modules/SecurityAndAccess/CertificatesStore';
+import BVToastMixin from '@/components/Mixins/BVToastMixin';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+
+export default {
+ name: 'ModalGenerateCsr',
+ components: { IconAdd, IconCheckmark },
+ mixins: [BVToastMixin, VuelidateMixin],
+ data() {
+ return {
+ form: {
+ certificateType: null,
+ country: null,
+ state: null,
+ city: null,
+ companyName: null,
+ companyUnit: null,
+ commonName: null,
+ challengePassword: null,
+ contactPerson: null,
+ emailAddress: null,
+ alternateName: [],
+ keyPairAlgorithm: null,
+ keyCurveId: null,
+ keyBitLength: null,
+ },
+ certificateOptions: CERTIFICATE_TYPES.reduce((arr, cert) => {
+ if (cert.type === 'TrustStore Certificate') return arr;
+ arr.push({
+ text: cert.label,
+ value: cert.type,
+ });
+ return arr;
+ }, []),
+ countryOptions: COUNTRY_LIST.map((country) => ({
+ text: country.label,
+ value: country.code,
+ })),
+ keyPairAlgorithmOptions: ['EC', 'RSA'],
+ keyCurveIdOptions: ['prime256v1', 'secp521r1', 'secp384r1'],
+ keyBitLengthOptions: [2048],
+ csrString: '',
+ csrStringCopied: false,
+ };
+ },
+ validations: {
+ form: {
+ certificateType: { required },
+ country: { required },
+ state: { required },
+ city: { required },
+ companyName: { required },
+ companyUnit: { required },
+ commonName: { required },
+ challengePassword: {},
+ contactPerson: {},
+ emailAddress: {},
+ alternateName: {},
+ keyPairAlgorithm: { required },
+ keyCurveId: {
+ reuired: requiredIf(function (form) {
+ return form.keyPairAlgorithm === 'EC';
+ }),
+ },
+ keyBitLength: {
+ reuired: requiredIf(function (form) {
+ return form.keyPairAlgorithm === 'RSA';
+ }),
+ },
+ },
+ },
+ methods: {
+ handleSubmit() {
+ this.$v.$touch();
+ if (this.$v.$invalid) return;
+ this.$store
+ .dispatch('certificates/generateCsr', this.form)
+ .then(({ data: { CSRString } }) => {
+ this.csrString = CSRString;
+ this.$bvModal.show('csr-string');
+ this.$v.$reset();
+ });
+ },
+ resetForm() {
+ for (let key of Object.keys(this.form)) {
+ if (key === 'alternateName') {
+ this.form[key] = [];
+ } else {
+ this.form[key] = null;
+ }
+ }
+ },
+ onOkGenerateCsrModal(bvModalEvt) {
+ // prevent modal close
+ bvModalEvt.preventDefault();
+ this.handleSubmit();
+ },
+ onHiddenCsrStringModal() {
+ this.csrString = '';
+ this.resetForm();
+ },
+ copyCsrString(bvModalEvt) {
+ // prevent modal close
+ bvModalEvt.preventDefault();
+ navigator.clipboard.writeText(this.csrString).then(() => {
+ // Show copied text for 5 seconds
+ this.csrStringCopied = true;
+ setTimeout(() => {
+ this.csrStringCopied = false;
+ }, 5000 /*5 seconds*/);
+ });
+ },
+ },
+};
+</script>
diff --git a/src/views/_sila/SecurityAndAccess/Certificates/ModalUploadCertificate.vue b/src/views/_sila/SecurityAndAccess/Certificates/ModalUploadCertificate.vue
new file mode 100644
index 00000000..f4db7a26
--- /dev/null
+++ b/src/views/_sila/SecurityAndAccess/Certificates/ModalUploadCertificate.vue
@@ -0,0 +1,168 @@
+<template>
+ <b-modal id="upload-certificate" ref="modal" @ok="onOk" @hidden="resetForm">
+ <template #modal-title>
+ <template v-if="certificate">
+ {{ $t('pageCertificates.replaceCertificate') }}
+ </template>
+ <template v-else>
+ {{ $t('pageCertificates.addNewCertificate') }}
+ </template>
+ </template>
+ <b-form>
+ <!-- Replace Certificate type -->
+ <template v-if="certificate !== null">
+ <dl class="mb-4">
+ <dt>{{ $t('pageCertificates.modal.certificateType') }}</dt>
+ <dd>{{ certificate.certificate }}</dd>
+ </dl>
+ </template>
+
+ <!-- Add new Certificate type -->
+ <template v-else>
+ <b-form-group
+ :label="$t('pageCertificates.modal.certificateType')"
+ label-for="certificate-type"
+ >
+ <b-form-select
+ id="certificate-type"
+ v-model="form.certificateType"
+ :options="certificateOptions"
+ :state="getValidationState($v.form.certificateType)"
+ @input="$v.form.certificateType.$touch()"
+ >
+ </b-form-select>
+ <b-form-invalid-feedback role="alert">
+ <template v-if="!$v.form.certificateType.required">
+ {{ $t('global.form.fieldRequired') }}
+ </template>
+ </b-form-invalid-feedback>
+ </b-form-group>
+ </template>
+
+ <b-form-group :label="$t('pageCertificates.modal.certificateFile')">
+ <form-file
+ id="certificate-file"
+ v-model="form.file"
+ accept=".pem"
+ :state="getValidationState($v.form.file)"
+ >
+ <template #invalid>
+ <b-form-invalid-feedback role="alert">
+ {{ $t('global.form.required') }}
+ </b-form-invalid-feedback>
+ </template>
+ </form-file>
+ </b-form-group>
+ </b-form>
+ <template #modal-ok>
+ <template v-if="certificate">
+ {{ $t('global.action.replace') }}
+ </template>
+ <template v-else>
+ {{ $t('global.action.add') }}
+ </template>
+ </template>
+ <template #modal-cancel>
+ {{ $t('global.action.cancel') }}
+ </template>
+ </b-modal>
+</template>
+
+<script>
+import { required, requiredIf } from 'vuelidate/lib/validators';
+import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
+
+import FormFile from '@/components/Global/FormFile';
+
+export default {
+ components: { FormFile },
+ mixins: [VuelidateMixin],
+ props: {
+ certificate: {
+ type: Object,
+ default: null,
+ validator: (prop) => {
+ if (prop === null) return true;
+ return (
+ Object.prototype.hasOwnProperty.call(prop, 'type') &&
+ Object.prototype.hasOwnProperty.call(prop, 'certificate')
+ );
+ },
+ },
+ },
+ data() {
+ return {
+ form: {
+ certificateType: null,
+ file: null,
+ },
+ };
+ },
+ computed: {
+ certificateTypes() {
+ return this.$store.getters['certificates/availableUploadTypes'];
+ },
+ certificateOptions() {
+ return this.certificateTypes.map(({ type, label }) => {
+ return {
+ text: label,
+ value: type,
+ };
+ });
+ },
+ },
+ watch: {
+ certificateOptions: function (options) {
+ if (options.length) {
+ this.form.certificateType = options[0].value;
+ }
+ },
+ },
+ validations() {
+ return {
+ form: {
+ certificateType: {
+ required: requiredIf(function () {
+ return !this.certificate;
+ }),
+ },
+ file: {
+ required,
+ },
+ },
+ };
+ },
+ methods: {
+ handleSubmit() {
+ this.$v.$touch();
+ if (this.$v.$invalid) return;
+ this.$emit('ok', {
+ addNew: !this.certificate,
+ file: this.form.file,
+ location: this.certificate ? this.certificate.location : null,
+ type: this.certificate
+ ? this.certificate.type
+ : this.form.certificateType,
+ });
+ this.closeModal();
+ },
+ closeModal() {
+ this.$nextTick(() => {
+ this.$refs.modal.hide();
+ });
+ },
+ resetForm() {
+ this.form.certificateType = this.certificateOptions.length
+ ? this.certificateOptions[0].value
+ : null;
+ this.form.file = null;
+ this.$v.$reset();
+ },
+ onOk(bvModalEvt) {
+ // prevent modal close
+ bvModalEvt.preventDefault();
+ this.handleSubmit();
+ },
+ },
+};
+</script>
diff --git a/src/views/_sila/SecurityAndAccess/Certificates/index.js b/src/views/_sila/SecurityAndAccess/Certificates/index.js
new file mode 100644
index 00000000..aff57b59
--- /dev/null
+++ b/src/views/_sila/SecurityAndAccess/Certificates/index.js
@@ -0,0 +1,2 @@
+import Certificates from './Certificates.vue';
+export default Certificates;