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="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=" 365 `data:application/json;charset=utf-8,` + 366 encodeURIComponent(`${csrString}`) 367 " 368 download="certificate.csr" 369 class="btn btn-primary" 370 > 371 {{ $t('global.action.download') }} 372 </a> 373 </template> 374 </b-modal> 375 </div> 376</template> 377 378<script> 379import IconAdd from '@carbon/icons-vue/es/add--alt/20'; 380import IconCheckmark from '@carbon/icons-vue/es/checkmark/20'; 381 382import { required, requiredIf } from 'vuelidate/lib/validators'; 383 384import { COUNTRY_LIST } from './CsrCountryCodes'; 385import { CERTIFICATE_TYPES } from '@/store/modules/SecurityAndAccess/CertificatesStore'; 386import BVToastMixin from '@/components/Mixins/BVToastMixin'; 387import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js'; 388 389export default { 390 name: 'ModalGenerateCsr', 391 components: { IconAdd, IconCheckmark }, 392 mixins: [BVToastMixin, VuelidateMixin], 393 data() { 394 return { 395 form: { 396 certificateType: null, 397 country: null, 398 state: null, 399 city: null, 400 companyName: null, 401 companyUnit: null, 402 commonName: null, 403 challengePassword: null, 404 contactPerson: null, 405 emailAddress: null, 406 alternateName: [], 407 keyPairAlgorithm: null, 408 keyCurveId: null, 409 keyBitLength: null, 410 }, 411 certificateOptions: CERTIFICATE_TYPES.reduce((arr, cert) => { 412 if (cert.type === 'TrustStore Certificate') return arr; 413 arr.push({ 414 text: cert.label, 415 value: cert.type, 416 }); 417 return arr; 418 }, []), 419 countryOptions: COUNTRY_LIST.map((country) => ({ 420 text: country.label, 421 value: country.code, 422 })), 423 keyPairAlgorithmOptions: ['EC', 'RSA'], 424 keyCurveIdOptions: ['prime256v1', 'secp521r1', 'secp384r1'], 425 keyBitLengthOptions: [2048], 426 csrString: '', 427 csrStringCopied: false, 428 }; 429 }, 430 validations: { 431 form: { 432 certificateType: { required }, 433 country: { required }, 434 state: { required }, 435 city: { required }, 436 companyName: { required }, 437 companyUnit: { required }, 438 commonName: { required }, 439 challengePassword: {}, 440 contactPerson: {}, 441 emailAddress: {}, 442 alternateName: {}, 443 keyPairAlgorithm: { required }, 444 keyCurveId: { 445 reuired: requiredIf(function (form) { 446 return form.keyPairAlgorithm === 'EC'; 447 }), 448 }, 449 keyBitLength: { 450 reuired: requiredIf(function (form) { 451 return form.keyPairAlgorithm === 'RSA'; 452 }), 453 }, 454 }, 455 }, 456 methods: { 457 handleSubmit() { 458 this.$v.$touch(); 459 if (this.$v.$invalid) return; 460 this.$store 461 .dispatch('certificates/generateCsr', this.form) 462 .then(({ data: { CSRString } }) => { 463 this.csrString = CSRString; 464 this.$bvModal.show('csr-string'); 465 this.$v.$reset(); 466 }); 467 }, 468 resetForm() { 469 for (let key of Object.keys(this.form)) { 470 if (key === 'alternateName') { 471 this.form[key] = []; 472 } else { 473 this.form[key] = null; 474 } 475 } 476 }, 477 onOkGenerateCsrModal(bvModalEvt) { 478 // prevent modal close 479 bvModalEvt.preventDefault(); 480 this.handleSubmit(); 481 }, 482 onHiddenCsrStringModal() { 483 this.csrString = ''; 484 this.resetForm(); 485 }, 486 copyCsrString(bvModalEvt) { 487 // prevent modal close 488 bvModalEvt.preventDefault(); 489 navigator.clipboard.writeText(this.csrString).then(() => { 490 // Show copied text for 5 seconds 491 this.csrStringCopied = true; 492 setTimeout(() => { 493 this.csrStringCopied = false; 494 }, 5000 /*5 seconds*/); 495 }); 496 }, 497 }, 498}; 499</script> 500