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