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