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