1<template> 2 <div> 3 <b-modal 4 id="generate-csr" 5 ref="modal" 6 size="lg" 7 no-stacking 8 :title="$t('pageCertificates.modal.generateACertificateSigningRequest')" 9 @ok="onOkGenerateCsrModal" 10 @cancel="resetForm" 11 @hidden="v$.$reset()" 12 > 13 <b-form id="generate-csr-form" novalidate> 14 <b-container fluid> 15 <b-row> 16 <b-col lg="9"> 17 <b-row> 18 <b-col lg="6"> 19 <b-form-group 20 :label="$t('pageCertificates.modal.certificateType')" 21 label-for="certificate-type" 22 > 23 <b-form-select 24 id="certificate-type" 25 v-model="form.certificateType" 26 data-test-id="modalGenerateCsr-select-certificateType" 27 :options="certificateOptions" 28 :state="getValidationState(v$.form.certificateType)" 29 @input="v$.form.certificateType.$touch()" 30 > 31 <template #first> 32 <b-form-select-option :value="null" disabled> 33 {{ $t('global.form.selectAnOption') }} 34 </b-form-select-option> 35 </template> 36 </b-form-select> 37 <b-form-invalid-feedback role="alert"> 38 {{ $t('global.form.fieldRequired') }} 39 </b-form-invalid-feedback> 40 </b-form-group> 41 </b-col> 42 <b-col lg="6"> 43 <b-form-group 44 :label="$t('pageCertificates.modal.country')" 45 label-for="country" 46 > 47 <b-form-select 48 id="country" 49 v-model="form.country" 50 data-test-id="modalGenerateCsr-select-country" 51 :options="countryOptions" 52 :state="getValidationState(v$.form.country)" 53 @input="v$.form.country.$touch()" 54 > 55 <template #first> 56 <b-form-select-option :value="null" disabled> 57 {{ $t('global.form.selectAnOption') }} 58 </b-form-select-option> 59 </template> 60 </b-form-select> 61 <b-form-invalid-feedback role="alert"> 62 {{ $t('global.form.fieldRequired') }} 63 </b-form-invalid-feedback> 64 </b-form-group> 65 </b-col> 66 </b-row> 67 <b-row> 68 <b-col lg="6"> 69 <b-form-group 70 :label="$t('pageCertificates.modal.state')" 71 label-for="state" 72 > 73 <b-form-input 74 id="state" 75 v-model="form.state" 76 type="text" 77 data-test-id="modalGenerateCsr-input-state" 78 :state="getValidationState(v$.form.state)" 79 /> 80 <b-form-invalid-feedback role="alert"> 81 {{ $t('global.form.fieldRequired') }} 82 </b-form-invalid-feedback> 83 </b-form-group> 84 </b-col> 85 <b-col lg="6"> 86 <b-form-group 87 :label="$t('pageCertificates.modal.city')" 88 label-for="city" 89 > 90 <b-form-input 91 id="city" 92 v-model="form.city" 93 type="text" 94 data-test-id="modalGenerateCsr-input-city" 95 :state="getValidationState(v$.form.city)" 96 /> 97 <b-form-invalid-feedback role="alert"> 98 {{ $t('global.form.fieldRequired') }} 99 </b-form-invalid-feedback> 100 </b-form-group> 101 </b-col> 102 </b-row> 103 <b-row> 104 <b-col lg="6"> 105 <b-form-group 106 :label="$t('pageCertificates.modal.companyName')" 107 label-for="company-name" 108 > 109 <b-form-input 110 id="company-name" 111 v-model="form.companyName" 112 type="text" 113 data-test-id="modalGenerateCsr-input-companyName" 114 :state="getValidationState(v$.form.companyName)" 115 /> 116 <b-form-invalid-feedback role="alert"> 117 {{ $t('global.form.fieldRequired') }} 118 </b-form-invalid-feedback> 119 </b-form-group> 120 </b-col> 121 <b-col lg="6"> 122 <b-form-group 123 :label="$t('pageCertificates.modal.companyUnit')" 124 label-for="company-unit" 125 > 126 <b-form-input 127 id="company-unit" 128 v-model="form.companyUnit" 129 type="text" 130 data-test-id="modalGenerateCsr-input-companyUnit" 131 :state="getValidationState(v$.form.companyUnit)" 132 /> 133 <b-form-invalid-feedback role="alert"> 134 {{ $t('global.form.fieldRequired') }} 135 </b-form-invalid-feedback> 136 </b-form-group> 137 </b-col> 138 </b-row> 139 <b-row> 140 <b-col lg="6"> 141 <b-form-group 142 :label="$t('pageCertificates.modal.commonName')" 143 label-for="common-name" 144 > 145 <b-form-input 146 id="common-name" 147 v-model="form.commonName" 148 type="text" 149 data-test-id="modalGenerateCsr-input-commonName" 150 :state="getValidationState(v$.form.commonName)" 151 /> 152 <b-form-invalid-feedback role="alert"> 153 {{ $t('global.form.fieldRequired') }} 154 </b-form-invalid-feedback> 155 </b-form-group> 156 </b-col> 157 <b-col lg="6"> 158 <b-form-group label-for="contact-person"> 159 <template #label> 160 {{ $t('pageCertificates.modal.contactPerson') }} - 161 <span class="form-text d-inline"> 162 {{ $t('global.form.optional') }} 163 </span> 164 </template> 165 <b-form-input 166 id="contact-person" 167 v-model="form.contactPerson" 168 type="text" 169 data-test-id="modalGenerateCsr-input-contactPerson" 170 /> 171 </b-form-group> 172 </b-col> 173 </b-row> 174 <b-row> 175 <b-col lg="6"> 176 <b-form-group label-for="email-address"> 177 <template #label> 178 {{ $t('pageCertificates.modal.emailAddress') }} - 179 <span class="form-text d-inline"> 180 {{ $t('global.form.optional') }} 181 </span> 182 </template> 183 <b-form-input 184 id="email-address" 185 v-model="form.emailAddress" 186 type="text" 187 data-test-id="modalGenerateCsr-input-emailAddress" 188 /> 189 </b-form-group> 190 </b-col> 191 </b-row> 192 <b-row> 193 <b-col lg="12"> 194 <b-form-group label-for="alternate-name"> 195 <template #label> 196 {{ $t('pageCertificates.modal.alternateName') }} - 197 <span class="form-text d-inline"> 198 {{ $t('global.form.optional') }} 199 </span> 200 </template> 201 <b-form-text id="alternate-name-help-block"> 202 {{ $t('pageCertificates.modal.alternateNameHelperText') }} 203 </b-form-text> 204 <b-form-tags 205 v-model="form.alternateName" 206 :remove-on-delete="true" 207 :tag-pills="true" 208 input-id="alternate-name" 209 size="lg" 210 separator=" " 211 :input-attrs="{ 212 'aria-describedby': 'alternate-name-help-block', 213 }" 214 :duplicate-tag-text=" 215 $t('pageCertificates.modal.duplicateAlternateName') 216 " 217 placeholder="" 218 data-test-id="modalGenerateCsr-input-alternateName" 219 > 220 <template #add-button-text> 221 <icon-add /> {{ $t('global.action.add') }} 222 </template> 223 </b-form-tags> 224 </b-form-group> 225 </b-col> 226 </b-row> 227 </b-col> 228 <b-col lg="3"> 229 <b-row> 230 <b-col lg="12"> 231 <p class="col-form-label"> 232 {{ $t('pageCertificates.modal.privateKey') }} 233 </p> 234 <b-form-group 235 :label="$t('pageCertificates.modal.keyPairAlgorithm')" 236 label-for="key-pair-algorithm" 237 > 238 <b-form-select 239 id="key-pair-algorithm" 240 v-model="form.keyPairAlgorithm" 241 data-test-id="modalGenerateCsr-select-keyPairAlgorithm" 242 :options="keyPairAlgorithmOptions" 243 :state="getValidationState(v$.form.keyPairAlgorithm)" 244 @input="v$.form.keyPairAlgorithm.$touch()" 245 > 246 <template #first> 247 <b-form-select-option :value="null" disabled> 248 {{ $t('global.form.selectAnOption') }} 249 </b-form-select-option> 250 </template> 251 </b-form-select> 252 <b-form-invalid-feedback role="alert"> 253 {{ $t('global.form.fieldRequired') }} 254 </b-form-invalid-feedback> 255 </b-form-group> 256 </b-col> 257 </b-row> 258 <b-row> 259 <b-col lg="12"> 260 <template v-if="v$.form.keyPairAlgorithm.$model === 'EC'"> 261 <b-form-group 262 :label="$t('pageCertificates.modal.keyCurveId')" 263 label-for="key-curve-id" 264 > 265 <b-form-select 266 id="key-curve-id" 267 v-model="form.keyCurveId" 268 data-test-id="modalGenerateCsr-select-keyCurveId" 269 :options="keyCurveIdOptions" 270 :state="getValidationState(v$.form.keyCurveId)" 271 @input="v$.form.keyCurveId.$touch()" 272 > 273 <template #first> 274 <b-form-select-option :value="null" disabled> 275 {{ $t('global.form.selectAnOption') }} 276 </b-form-select-option> 277 </template> 278 </b-form-select> 279 <b-form-invalid-feedback role="alert"> 280 {{ $t('global.form.fieldRequired') }} 281 </b-form-invalid-feedback> 282 </b-form-group> 283 </template> 284 <template v-if="v$.form.keyPairAlgorithm.$model === 'RSA'"> 285 <b-form-group 286 :label="$t('pageCertificates.modal.keyBitLength')" 287 label-for="key-bit-length" 288 > 289 <b-form-select 290 id="key-bit-length" 291 v-model="form.keyBitLength" 292 data-test-id="modalGenerateCsr-select-keyBitLength" 293 :options="keyBitLengthOptions" 294 :state="getValidationState(v$.form.keyBitLength)" 295 @input="v$.form.keyBitLength.$touch()" 296 > 297 <template #first> 298 <b-form-select-option :value="null" disabled> 299 {{ $t('global.form.selectAnOption') }} 300 </b-form-select-option> 301 </template> 302 </b-form-select> 303 <b-form-invalid-feedback role="alert"> 304 {{ $t('global.form.fieldRequired') }} 305 </b-form-invalid-feedback> 306 </b-form-group> 307 </template> 308 </b-col> 309 </b-row> 310 </b-col> 311 </b-row> 312 </b-container> 313 </b-form> 314 <template #modal-footer="{ ok, cancel }"> 315 <b-button variant="secondary" @click="cancel()"> 316 {{ $t('global.action.cancel') }} 317 </b-button> 318 <b-button 319 form="generate-csr-form" 320 type="submit" 321 variant="primary" 322 data-test-id="modalGenerateCsr-button-ok" 323 @click="ok()" 324 > 325 {{ $t('pageCertificates.generateCsr') }} 326 </b-button> 327 </template> 328 </b-modal> 329 <b-modal 330 id="csr-string" 331 no-stacking 332 size="lg" 333 :title="$t('pageCertificates.modal.certificateSigningRequest')" 334 @hidden="onHiddenCsrStringModal" 335 > 336 {{ csrString }} 337 <template #modal-footer> 338 <b-btn variant="secondary" @click="copyCsrString"> 339 <template v-if="csrStringCopied"> 340 <icon-checkmark /> 341 {{ $t('global.status.copied') }} 342 </template> 343 <template v-else> 344 {{ $t('global.action.copy') }} 345 </template> 346 </b-btn> 347 <a 348 :href=" 349 `data:application/json;charset=utf-8,` + 350 encodeURIComponent(`${csrString}`) 351 " 352 download="certificate.csr" 353 class="btn btn-primary" 354 > 355 {{ $t('global.action.download') }} 356 </a> 357 </template> 358 </b-modal> 359 </div> 360</template> 361 362<script> 363import IconAdd from '@carbon/icons-vue/es/add--alt/20'; 364import IconCheckmark from '@carbon/icons-vue/es/checkmark/20'; 365 366import { required, requiredIf } from '@vuelidate/validators'; 367 368import { COUNTRY_LIST } from './CsrCountryCodes'; 369import BVToastMixin from '@/components/Mixins/BVToastMixin'; 370import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; 371import { useVuelidate } from '@vuelidate/core'; 372import { useI18n } from 'vue-i18n'; 373 374export default { 375 name: 'ModalGenerateCsr', 376 components: { IconAdd, IconCheckmark }, 377 mixins: [BVToastMixin, VuelidateMixin], 378 setup() { 379 return { 380 v$: useVuelidate(), 381 }; 382 }, 383 data() { 384 return { 385 $t: useI18n().t, 386 form: { 387 certificateType: null, 388 country: null, 389 state: null, 390 city: null, 391 companyName: null, 392 companyUnit: null, 393 commonName: null, 394 contactPerson: null, 395 emailAddress: null, 396 alternateName: [], 397 keyPairAlgorithm: null, 398 keyCurveId: null, 399 keyBitLength: null, 400 }, 401 countryOptions: COUNTRY_LIST.map((country) => ({ 402 text: country.label, 403 value: country.code, 404 })), 405 keyPairAlgorithmOptions: ['EC', 'RSA'], 406 keyCurveIdOptions: ['prime256v1', 'secp521r1', 'secp384r1'], 407 keyBitLengthOptions: [2048], 408 csrString: '', 409 csrStringCopied: false, 410 }; 411 }, 412 computed: { 413 certificateTypes() { 414 return this.$store.getters['certificates/certificateTypes']; 415 }, 416 certificateOptions() { 417 return this.certificateTypes.reduce((arr, cert) => { 418 if (cert.type === 'TrustStore Certificate') return arr; 419 arr.push({ 420 text: cert.label, 421 value: cert.type, 422 }); 423 return arr; 424 }, []); 425 }, 426 }, 427 validations: { 428 form: { 429 certificateType: { required }, 430 country: { required }, 431 state: { required }, 432 city: { required }, 433 companyName: { required }, 434 companyUnit: { required }, 435 commonName: { required }, 436 contactPerson: {}, 437 emailAddress: {}, 438 alternateName: {}, 439 keyPairAlgorithm: { required }, 440 keyCurveId: { 441 reuired: requiredIf(function (form) { 442 return form.keyPairAlgorithm === 'EC'; 443 }), 444 }, 445 keyBitLength: { 446 reuired: requiredIf(function (form) { 447 return form.keyPairAlgorithm === 'RSA'; 448 }), 449 }, 450 }, 451 }, 452 methods: { 453 handleSubmit() { 454 this.v$.$touch(); 455 if (this.v$.$invalid) return; 456 this.$store 457 .dispatch('certificates/generateCsr', this.form) 458 .then(({ data: { CSRString } }) => { 459 this.csrString = CSRString; 460 this.$bvModal.show('csr-string'); 461 this.v$.$reset(); 462 }); 463 }, 464 resetForm() { 465 for (let key of Object.keys(this.form)) { 466 if (key === 'alternateName') { 467 this.form[key] = []; 468 } else { 469 this.form[key] = null; 470 } 471 } 472 }, 473 onOkGenerateCsrModal(bvModalEvt) { 474 // prevent modal close 475 bvModalEvt.preventDefault(); 476 this.handleSubmit(); 477 }, 478 onHiddenCsrStringModal() { 479 this.csrString = ''; 480 this.resetForm(); 481 }, 482 copyCsrString(bvModalEvt) { 483 // prevent modal close 484 bvModalEvt.preventDefault(); 485 navigator.clipboard.writeText(this.csrString).then(() => { 486 // Show copied text for 5 seconds 487 this.csrStringCopied = true; 488 setTimeout(() => { 489 this.csrStringCopied = false; 490 }, 5000 /*5 seconds*/); 491 }); 492 }, 493 }, 494}; 495</script> 496