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 @submit.prevent="handleSubmit"> 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="challenge-password"> 159 <template #label> 160 {{ $t('pageCertificates.modal.challengePassword') }} - 161 <span class="form-text d-inline"> 162 {{ $t('global.form.optional') }} 163 </span> 164 </template> 165 <b-form-input 166 id="challenge-password" 167 v-model="form.challengePassword" 168 type="text" 169 data-test-id="modalGenerateCsr-input-challengePassword" 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="contact-person"> 177 <template #label> 178 {{ $t('pageCertificates.modal.contactPerson') }} - 179 <span class="form-text d-inline"> 180 {{ $t('global.form.optional') }} 181 </span> 182 </template> 183 <b-form-input 184 id="contact-person" 185 v-model="form.contactPerson" 186 type="text" 187 data-test-id="modalGenerateCsr-input-contactPerson" 188 /> 189 </b-form-group> 190 </b-col> 191 <b-col lg="6"> 192 <b-form-group label-for="email-address"> 193 <template #label> 194 {{ $t('pageCertificates.modal.emailAddress') }} - 195 <span class="form-text d-inline"> 196 {{ $t('global.form.optional') }} 197 </span> 198 </template> 199 <b-form-input 200 id="email-address" 201 v-model="form.emailAddress" 202 type="text" 203 data-test-id="modalGenerateCsr-input-emailAddress" 204 /> 205 </b-form-group> 206 </b-col> 207 </b-row> 208 <b-row> 209 <b-col lg="12"> 210 <b-form-group label-for="alternate-name"> 211 <template #label> 212 {{ $t('pageCertificates.modal.alternateName') }} - 213 <span class="form-text d-inline"> 214 {{ $t('global.form.optional') }} 215 </span> 216 </template> 217 <b-form-text id="alternate-name-help-block"> 218 {{ $t('pageCertificates.modal.alternateNameHelperText') }} 219 </b-form-text> 220 <b-form-tags 221 v-model="form.alternateName" 222 :remove-on-delete="true" 223 :tag-pills="true" 224 input-id="alternate-name" 225 size="lg" 226 separator=" " 227 :input-attrs="{ 228 'aria-describedby': 'alternate-name-help-block', 229 }" 230 :duplicate-tag-text=" 231 $t('pageCertificates.modal.duplicateAlternateName') 232 " 233 placeholder="" 234 data-test-id="modalGenerateCsr-input-alternateName" 235 > 236 <template #add-button-text> 237 <icon-add /> {{ $t('global.action.add') }} 238 </template> 239 </b-form-tags> 240 </b-form-group> 241 </b-col> 242 </b-row> 243 </b-col> 244 <b-col lg="3"> 245 <b-row> 246 <b-col lg="12"> 247 <p class="col-form-label"> 248 {{ $t('pageCertificates.modal.privateKey') }} 249 </p> 250 <b-form-group 251 :label="$t('pageCertificates.modal.keyPairAlgorithm')" 252 label-for="key-pair-algorithm" 253 > 254 <b-form-select 255 id="key-pair-algorithm" 256 v-model="form.keyPairAlgorithm" 257 data-test-id="modalGenerateCsr-select-keyPairAlgorithm" 258 :options="keyPairAlgorithmOptions" 259 :state="getValidationState($v.form.keyPairAlgorithm)" 260 @input="$v.form.keyPairAlgorithm.$touch()" 261 > 262 <template #first> 263 <b-form-select-option :value="null" disabled> 264 {{ $t('global.form.selectAnOption') }} 265 </b-form-select-option> 266 </template> 267 </b-form-select> 268 <b-form-invalid-feedback role="alert"> 269 {{ $t('global.form.fieldRequired') }} 270 </b-form-invalid-feedback> 271 </b-form-group> 272 </b-col> 273 </b-row> 274 <b-row> 275 <b-col lg="12"> 276 <template v-if="$v.form.keyPairAlgorithm.$model === 'EC'"> 277 <b-form-group 278 :label="$t('pageCertificates.modal.keyCurveId')" 279 label-for="key-curve-id" 280 > 281 <b-form-select 282 id="key-curve-id" 283 v-model="form.keyCurveId" 284 data-test-id="modalGenerateCsr-select-keyCurveId" 285 :options="keyCurveIdOptions" 286 :state="getValidationState($v.form.keyCurveId)" 287 @input="$v.form.keyCurveId.$touch()" 288 > 289 <template #first> 290 <b-form-select-option :value="null" disabled> 291 {{ $t('global.form.selectAnOption') }} 292 </b-form-select-option> 293 </template> 294 </b-form-select> 295 <b-form-invalid-feedback role="alert"> 296 {{ $t('global.form.fieldRequired') }} 297 </b-form-invalid-feedback> 298 </b-form-group> 299 </template> 300 <template v-if="$v.form.keyPairAlgorithm.$model === 'RSA'"> 301 <b-form-group 302 :label="$t('pageCertificates.modal.keyBitLength')" 303 label-for="key-bit-length" 304 > 305 <b-form-select 306 id="key-bit-length" 307 v-model="form.keyBitLength" 308 data-test-id="modalGenerateCsr-select-keyBitLength" 309 :options="keyBitLengthOptions" 310 :state="getValidationState($v.form.keyBitLength)" 311 @input="$v.form.keyBitLength.$touch()" 312 > 313 <template #first> 314 <b-form-select-option :value="null" disabled> 315 {{ $t('global.form.selectAnOption') }} 316 </b-form-select-option> 317 </template> 318 </b-form-select> 319 <b-form-invalid-feedback role="alert"> 320 {{ $t('global.form.fieldRequired') }} 321 </b-form-invalid-feedback> 322 </b-form-group> 323 </template> 324 </b-col> 325 </b-row> 326 </b-col> 327 </b-row> 328 </b-container> 329 </b-form> 330 <template #modal-footer="{ ok, cancel }"> 331 <b-button variant="secondary" @click="cancel()"> 332 {{ $t('global.action.cancel') }} 333 </b-button> 334 <b-button 335 form="generate-csr-form" 336 type="submit" 337 variant="primary" 338 data-test-id="modalGenerateCsr-button-ok" 339 @click="ok()" 340 > 341 {{ $t('pageCertificates.generateCsr') }} 342 </b-button> 343 </template> 344 </b-modal> 345 <b-modal 346 id="csr-string" 347 no-stacking 348 size="lg" 349 :title="$t('pageCertificates.modal.certificateSigningRequest')" 350 @hidden="onHiddenCsrStringModal" 351 > 352 {{ csrString }} 353 <template #modal-footer> 354 <b-btn variant="secondary" @click="copyCsrString"> 355 <template v-if="csrStringCopied"> 356 <icon-checkmark /> 357 {{ $t('global.status.copied') }} 358 </template> 359 <template v-else> 360 {{ $t('global.action.copy') }} 361 </template> 362 </b-btn> 363 <a 364 :href="`data:text/json;charset=utf-8,${csrString}`" 365 download="certificate.txt" 366 class="btn btn-primary" 367 > 368 {{ $t('global.action.download') }} 369 </a> 370 </template> 371 </b-modal> 372 </div> 373</template> 374 375<script> 376import IconAdd from '@carbon/icons-vue/es/add--alt/20'; 377import IconCheckmark from '@carbon/icons-vue/es/checkmark/20'; 378 379import { required, requiredIf } from 'vuelidate/lib/validators'; 380 381import { COUNTRY_LIST } from './CsrCountryCodes'; 382import { CERTIFICATE_TYPES } from '@/store/modules/SecurityAndAccess/CertificatesStore'; 383import BVToastMixin from '@/components/Mixins/BVToastMixin'; 384import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; 385 386export default { 387 name: 'ModalGenerateCsr', 388 components: { IconAdd, IconCheckmark }, 389 mixins: [BVToastMixin, VuelidateMixin], 390 data() { 391 return { 392 form: { 393 certificateType: null, 394 country: null, 395 state: null, 396 city: null, 397 companyName: null, 398 companyUnit: null, 399 commonName: null, 400 challengePassword: null, 401 contactPerson: null, 402 emailAddress: null, 403 alternateName: [], 404 keyPairAlgorithm: null, 405 keyCurveId: null, 406 keyBitLength: null, 407 }, 408 certificateOptions: CERTIFICATE_TYPES.reduce((arr, cert) => { 409 if (cert.type === 'TrustStore Certificate') return arr; 410 arr.push({ 411 text: cert.label, 412 value: cert.type, 413 }); 414 return arr; 415 }, []), 416 countryOptions: COUNTRY_LIST.map((country) => ({ 417 text: country.label, 418 value: country.code, 419 })), 420 keyPairAlgorithmOptions: ['EC', 'RSA'], 421 keyCurveIdOptions: ['prime256v1', 'secp521r1', 'secp384r1'], 422 keyBitLengthOptions: [2048], 423 csrString: '', 424 csrStringCopied: false, 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 challengePassword: {}, 437 contactPerson: {}, 438 emailAddress: {}, 439 alternateName: {}, 440 keyPairAlgorithm: { required }, 441 keyCurveId: { 442 reuired: requiredIf(function (form) { 443 return form.keyPairAlgorithm === 'EC'; 444 }), 445 }, 446 keyBitLength: { 447 reuired: requiredIf(function (form) { 448 return form.keyPairAlgorithm === 'RSA'; 449 }), 450 }, 451 }, 452 }, 453 methods: { 454 handleSubmit() { 455 this.$v.$touch(); 456 if (this.$v.$invalid) return; 457 this.$store 458 .dispatch('certificates/generateCsr', this.form) 459 .then(({ data: { CSRString } }) => { 460 this.csrString = CSRString; 461 this.$bvModal.show('csr-string'); 462 this.$v.$reset(); 463 }); 464 }, 465 resetForm() { 466 for (let key of Object.keys(this.form)) { 467 if (key === 'alternateName') { 468 this.form[key] = []; 469 } else { 470 this.form[key] = null; 471 } 472 } 473 }, 474 onOkGenerateCsrModal(bvModalEvt) { 475 // prevent modal close 476 bvModalEvt.preventDefault(); 477 this.handleSubmit(); 478 }, 479 onHiddenCsrStringModal() { 480 this.csrString = ''; 481 this.resetForm(); 482 }, 483 copyCsrString(bvModalEvt) { 484 // prevent modal close 485 bvModalEvt.preventDefault(); 486 navigator.clipboard.writeText(this.csrString).then(() => { 487 // Show copied text for 5 seconds 488 this.csrStringCopied = true; 489 setTimeout(() => { 490 this.csrStringCopied = false; 491 }, 5000 /*5 seconds*/); 492 }); 493 }, 494 }, 495}; 496</script> 497