diff options
author | Yoshie Muranaka <yoshiemuranaka@gmail.com> | 2020-03-25 01:25:24 +0300 |
---|---|---|
committer | Derick Montague <derick.montague@ibm.com> | 2020-04-08 21:58:55 +0300 |
commit | 37393810fa1ed2ae69ec05d0b19887e27760d48e (patch) | |
tree | 55381170946aed7c856de354d7ae8e0614fadc30 /src/views/AccessControl | |
parent | b346406f3aa407869a3c868fb14322fc529673a3 (diff) | |
download | webui-vue-37393810fa1ed2ae69ec05d0b19887e27760d48e.tar.xz |
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
Diffstat (limited to 'src/views/AccessControl')
3 files changed, 375 insertions, 0 deletions
diff --git a/src/views/AccessControl/SslCertificates/ModalUploadCertificate.vue b/src/views/AccessControl/SslCertificates/ModalUploadCertificate.vue new file mode 100644 index 00000000..653a232f --- /dev/null +++ b/src/views/AccessControl/SslCertificates/ModalUploadCertificate.vue @@ -0,0 +1,164 @@ +<template> + <b-modal id="upload-certificate" ref="modal" @ok="onOk" @hidden="resetForm"> + <template v-slot:modal-title> + <template v-if="certificate"> + {{ $t('pageSslCertificates.replaceCertificate') }} + </template> + <template v-else> + {{ $t('pageSslCertificates.addNewCertificate') }} + </template> + </template> + <b-form> + <!-- Replace Certificate type --> + <template v-if="certificate !== null"> + <dl class="mb-4"> + <dt>{{ $t('pageSslCertificates.modal.certificateType') }}</dt> + <dd>{{ certificate.certificate }}</dd> + </dl> + </template> + + <!-- Add new Certificate type --> + <template v-else> + <b-form-group + :label="$t('pageSslCertificates.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('pageSslCertificates.modal.certificateFile')" + label-for="certificate-file" + > + <b-form-file + id="certificate-file" + v-model="form.file" + accept=".pem" + plain + :state="getValidationState($v.form.file)" + /> + <b-form-invalid-feedback role="alert"> + <template v-if="!$v.form.file.required"> + {{ $t('global.form.required') }} + </template> + </b-form-invalid-feedback> + </b-form-group> + </b-form> + <template v-slot:modal-ok> + <template v-if="certificate"> + {{ $t('global.action.replace') }} + </template> + <template v-else> + {{ $t('global.action.add') }} + </template> + </template> + </b-modal> +</template> + +<script> +import { required, requiredIf } from 'vuelidate/lib/validators'; +import VuelidateMixin from '../../../components/Mixins/VuelidateMixin.js'; + +export default { + mixins: [VuelidateMixin], + props: { + certificate: { + type: Object, + default: null, + validator: prop => { + if (prop === null) return true; + return ( + prop.hasOwnProperty('type') && prop.hasOwnProperty('certificate') + ); + } + } + }, + data() { + return { + form: { + certificateType: null, + file: null + } + }; + }, + computed: { + certificateTypes() { + return this.$store.getters['sslCertificates/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/AccessControl/SslCertificates/SslCertificates.vue b/src/views/AccessControl/SslCertificates/SslCertificates.vue new file mode 100644 index 00000000..ae28271f --- /dev/null +++ b/src/views/AccessControl/SslCertificates/SslCertificates.vue @@ -0,0 +1,209 @@ +<template> + <b-container fluid> + <page-title /> + <b-row> + <b-col xl="9" class="text-right"> + <b-button + variant="primary" + :disabled="certificatesForUpload.length === 0" + @click="initModalUploadCertificate(null)" + > + <icon-add /> + {{ $t('pageSslCertificates.addNewCertificate') }} + </b-button> + </b-col> + </b-row> + <b-row> + <b-col xl="9"> + <b-table :fields="fields" :items="tableItems"> + <template v-slot:cell(validFrom)="{ value }"> + {{ value | formatDate }} + </template> + + <template v-slot:cell(validUntil)="{ value }"> + {{ value | formatDate }} + </template> + + <template v-slot: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:tableAction="onTableRowAction($event, item)" + > + <template v-slot: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" /> + </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 ModalUploadCertificate from './ModalUploadCertificate'; +import PageTitle from '../../../components/Global/PageTitle'; +import TableRowAction from '../../../components/Global/TableRowAction'; + +import BVToastMixin from '../../../components/Mixins/BVToastMixin'; + +export default { + name: 'SslCertificates', + components: { + IconAdd, + IconReplace, + IconTrashcan, + ModalUploadCertificate, + PageTitle, + TableRowAction + }, + mixins: [BVToastMixin], + data() { + return { + modalCertificate: null, + fields: [ + { + key: 'certificate', + label: this.$t('pageSslCertificates.table.certificate') + }, + { + key: 'issuedBy', + label: this.$t('pageSslCertificates.table.issuedBy') + }, + { + key: 'issuedTo', + label: this.$t('pageSslCertificates.table.issuedTo') + }, + { + key: 'validFrom', + label: this.$t('pageSslCertificates.table.validFrom') + }, + { + key: 'validUntil', + label: this.$t('pageSslCertificates.table.validUntil') + }, + { + key: 'actions', + label: '', + tdClass: 'text-right' + } + ] + }; + }, + computed: { + certificates() { + return this.$store.getters['sslCertificates/allCertificates']; + }, + tableItems() { + return this.certificates.map(certificate => { + return { + ...certificate, + actions: [ + { + value: 'replace', + title: this.$t('pageSslCertificates.replaceCertificate') + }, + { + value: 'delete', + title: this.$t('pageSslCertificates.deleteCertificate'), + enabled: + certificate.type === 'TrustStore Certificate' ? true : false + } + ] + }; + }); + }, + certificatesForUpload() { + return this.$store.getters['sslCertificates/availableUploadTypes']; + } + }, + created() { + this.$store.dispatch('sslCertificates/getCertificates'); + }, + 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('pageSslCertificates.modal.deleteConfirmMessage', { + issuedBy: certificate.issuedBy, + certificate: certificate.certificate + }), + { + title: this.$t('pageSslCertificates.deleteCertificate'), + okTitle: this.$t('global.action.delete') + } + ) + .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.$store + .dispatch('sslCertificates/addNewCertificate', { file, type }) + .then(success => this.successToast(success)) + .catch(({ message }) => this.errorToast(message)); + }, + replaceCertificate(file, type, location) { + const reader = new FileReader(); + reader.readAsBinaryString(file); + reader.onloadend = event => { + const certificateString = event.target.result; + this.$store + .dispatch('sslCertificates/replaceCertificate', { + certificateString, + type, + location + }) + .then(success => this.successToast(success)) + .catch(({ message }) => this.errorToast(message)); + }; + }, + deleteCertificate({ type, location }) { + this.$store + .dispatch('sslCertificates/deleteCertificate', { + type, + location + }) + .then(success => this.successToast(success)) + .catch(({ message }) => this.errorToast(message)); + } + } +}; +</script> diff --git a/src/views/AccessControl/SslCertificates/index.js b/src/views/AccessControl/SslCertificates/index.js new file mode 100644 index 00000000..03daa565 --- /dev/null +++ b/src/views/AccessControl/SslCertificates/index.js @@ -0,0 +1,2 @@ +import SslCertificates from './SslCertificates.vue'; +export default SslCertificates; |