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="
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