1<template>
2  <b-modal id="modal-user" ref="modal" @hidden="resetForm">
3    <template #modal-title>
4      <template v-if="newUser">
5        {{ $t('pageUserManagement.addUser') }}
6      </template>
7      <template v-else>
8        {{ $t('pageUserManagement.editUser') }}
9      </template>
10    </template>
11    <b-form id="form-user" novalidate @submit.prevent="handleSubmit">
12      <b-container>
13        <!-- Manual unlock form control -->
14        <b-row v-if="!newUser && manualUnlockPolicy && user.Locked">
15          <b-col sm="9">
16            <alert :show="true" variant="warning" small>
17              <template v-if="!$v.form.manualUnlock.$dirty">
18                {{ $t('pageUserManagement.modal.accountLocked') }}
19              </template>
20              <template v-else>
21                {{ $t('pageUserManagement.modal.clickSaveToUnlockAccount') }}
22              </template>
23            </alert>
24          </b-col>
25          <b-col sm="3">
26            <input
27              v-model="form.manualUnlock"
28              data-test-id="userManagement-input-manualUnlock"
29              type="hidden"
30              value="false"
31            />
32            <b-button
33              variant="primary"
34              :disabled="$v.form.manualUnlock.$dirty"
35              data-test-id="userManagement-button-manualUnlock"
36              @click="$v.form.manualUnlock.$touch()"
37            >
38              {{ $t('pageUserManagement.modal.unlock') }}
39            </b-button>
40          </b-col>
41        </b-row>
42        <b-row>
43          <b-col>
44            <b-form-group :label="$t('pageUserManagement.modal.accountStatus')">
45              <b-form-radio
46                v-model="form.status"
47                name="user-status"
48                :value="true"
49                data-test-id="userManagement-radioButton-statusEnabled"
50                @input="$v.form.status.$touch()"
51              >
52                {{ $t('global.status.enabled') }}
53              </b-form-radio>
54              <b-form-radio
55                v-model="form.status"
56                name="user-status"
57                data-test-id="userManagement-radioButton-statusDisabled"
58                :value="false"
59                @input="$v.form.status.$touch()"
60              >
61                {{ $t('global.status.disabled') }}
62              </b-form-radio>
63            </b-form-group>
64            <b-form-group
65              :label="$t('pageUserManagement.modal.username')"
66              label-for="username"
67            >
68              <b-form-text id="username-help-block">
69                {{ $t('pageUserManagement.modal.cannotStartWithANumber') }}
70                <br />
71                {{
72                  $t(
73                    'pageUserManagement.modal.noSpecialCharactersExceptUnderscore'
74                  )
75                }}
76              </b-form-text>
77              <b-form-input
78                id="username"
79                v-model="form.username"
80                type="text"
81                aria-describedby="username-help-block"
82                data-test-id="userManagement-input-username"
83                :state="getValidationState($v.form.username)"
84                :disabled="!newUser && originalUsername === 'root'"
85                @input="$v.form.username.$touch()"
86              />
87              <b-form-invalid-feedback role="alert">
88                <template v-if="!$v.form.username.required">
89                  {{ $t('global.form.fieldRequired') }}
90                </template>
91                <template v-else-if="!$v.form.username.maxLength">
92                  {{
93                    $t('global.form.lengthMustBeBetween', { min: 1, max: 16 })
94                  }}
95                </template>
96                <template v-else-if="!$v.form.username.pattern">
97                  {{ $t('global.form.invalidFormat') }}
98                </template>
99              </b-form-invalid-feedback>
100            </b-form-group>
101            <b-form-group
102              :label="$t('pageUserManagement.modal.privilege')"
103              label-for="privilege"
104            >
105              <b-form-select
106                id="privilege"
107                v-model="form.privilege"
108                :options="privilegeTypes"
109                data-test-id="userManagement-select-privilege"
110                :state="getValidationState($v.form.privilege)"
111                @input="$v.form.privilege.$touch()"
112              >
113                <template #first>
114                  <b-form-select-option :value="null" disabled>
115                    {{ $t('global.form.selectAnOption') }}
116                  </b-form-select-option>
117                </template>
118              </b-form-select>
119              <b-form-invalid-feedback role="alert">
120                <template v-if="!$v.form.privilege.required">
121                  {{ $t('global.form.fieldRequired') }}
122                </template>
123              </b-form-invalid-feedback>
124            </b-form-group>
125          </b-col>
126          <b-col>
127            <b-form-group
128              :label="$t('pageUserManagement.modal.userPassword')"
129              label-for="password"
130            >
131              <b-form-text id="password-help-block">
132                {{
133                  $t('pageUserManagement.modal.passwordMustBeBetween', {
134                    min: passwordRequirements.minLength,
135                    max: passwordRequirements.maxLength,
136                  })
137                }}
138              </b-form-text>
139              <input-password-toggle>
140                <b-form-input
141                  id="password"
142                  v-model="form.password"
143                  type="password"
144                  data-test-id="userManagement-input-password"
145                  aria-describedby="password-help-block"
146                  :state="getValidationState($v.form.password)"
147                  class="form-control-with-button"
148                  @input="$v.form.password.$touch()"
149                />
150                <b-form-invalid-feedback role="alert">
151                  <template v-if="!$v.form.password.required">
152                    {{ $t('global.form.fieldRequired') }}
153                  </template>
154                  <template
155                    v-if="
156                      !$v.form.password.minLength || !$v.form.password.maxLength
157                    "
158                  >
159                    {{
160                      $t('pageUserManagement.modal.passwordMustBeBetween', {
161                        min: passwordRequirements.minLength,
162                        max: passwordRequirements.maxLength,
163                      })
164                    }}
165                  </template>
166                </b-form-invalid-feedback>
167              </input-password-toggle>
168            </b-form-group>
169            <b-form-group
170              :label="$t('pageUserManagement.modal.confirmUserPassword')"
171              label-for="password-confirmation"
172            >
173              <input-password-toggle>
174                <b-form-input
175                  id="password-confirmation"
176                  v-model="form.passwordConfirmation"
177                  data-test-id="userManagement-input-passwordConfirmation"
178                  type="password"
179                  :state="getValidationState($v.form.passwordConfirmation)"
180                  class="form-control-with-button"
181                  @input="$v.form.passwordConfirmation.$touch()"
182                />
183                <b-form-invalid-feedback role="alert">
184                  <template v-if="!$v.form.passwordConfirmation.required">
185                    {{ $t('global.form.fieldRequired') }}
186                  </template>
187                  <template
188                    v-else-if="!$v.form.passwordConfirmation.sameAsPassword"
189                  >
190                    {{ $t('pageUserManagement.modal.passwordsDoNotMatch') }}
191                  </template>
192                </b-form-invalid-feedback>
193              </input-password-toggle>
194            </b-form-group>
195          </b-col>
196        </b-row>
197      </b-container>
198    </b-form>
199    <template #modal-footer="{ cancel }">
200      <b-button
201        variant="secondary"
202        data-test-id="userManagement-button-cancel"
203        @click="cancel()"
204      >
205        {{ $t('global.action.cancel') }}
206      </b-button>
207      <b-button
208        form="form-user"
209        data-test-id="userManagement-button-submit"
210        type="submit"
211        variant="primary"
212        @click="onOk"
213      >
214        <template v-if="newUser">
215          {{ $t('pageUserManagement.addUser') }}
216        </template>
217        <template v-else>
218          {{ $t('global.action.save') }}
219        </template>
220      </b-button>
221    </template>
222  </b-modal>
223</template>
224
225<script>
226import {
227  required,
228  maxLength,
229  minLength,
230  sameAs,
231  helpers,
232  requiredIf,
233} from 'vuelidate/lib/validators';
234import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
235import InputPasswordToggle from '@/components/Global/InputPasswordToggle';
236import Alert from '@/components/Global/Alert';
237
238export default {
239  components: { Alert, InputPasswordToggle },
240  mixins: [VuelidateMixin],
241  props: {
242    user: {
243      type: Object,
244      default: null,
245    },
246    passwordRequirements: {
247      type: Object,
248      required: true,
249    },
250  },
251  data() {
252    return {
253      originalUsername: '',
254      form: {
255        status: true,
256        username: '',
257        privilege: null,
258        password: '',
259        passwordConfirmation: '',
260        manualUnlock: false,
261      },
262    };
263  },
264  computed: {
265    newUser() {
266      return this.user ? false : true;
267    },
268    accountSettings() {
269      return this.$store.getters['userManagement/accountSettings'];
270    },
271    manualUnlockPolicy() {
272      return !this.accountSettings.accountLockoutDuration;
273    },
274    privilegeTypes() {
275      return this.$store.getters['userManagement/accountRoles'];
276    },
277  },
278  watch: {
279    user: function (value) {
280      if (value === null) return;
281      this.originalUsername = value.username;
282      this.form.username = value.username;
283      this.form.status = value.Enabled;
284      this.form.privilege = value.privilege;
285    },
286  },
287  validations() {
288    return {
289      form: {
290        status: {
291          required,
292        },
293        username: {
294          required,
295          maxLength: maxLength(16),
296          pattern: helpers.regex('pattern', /^([a-zA-Z_][a-zA-Z0-9_]*)/),
297        },
298        privilege: {
299          required,
300        },
301        password: {
302          required: requiredIf(function () {
303            return this.requirePassword();
304          }),
305          minLength: minLength(this.passwordRequirements.minLength),
306          maxLength: maxLength(this.passwordRequirements.maxLength),
307        },
308        passwordConfirmation: {
309          required: requiredIf(function () {
310            return this.requirePassword();
311          }),
312          sameAsPassword: sameAs('password'),
313        },
314        manualUnlock: {},
315      },
316    };
317  },
318  methods: {
319    handleSubmit() {
320      let userData = {};
321
322      if (this.newUser) {
323        this.$v.$touch();
324        if (this.$v.$invalid) return;
325        userData.username = this.form.username;
326        userData.status = this.form.status;
327        userData.privilege = this.form.privilege;
328        userData.password = this.form.password;
329      } else {
330        if (this.$v.$invalid) return;
331        userData.originalUsername = this.originalUsername;
332        if (this.$v.form.status.$dirty) {
333          userData.status = this.form.status;
334        }
335        if (this.$v.form.username.$dirty) {
336          userData.username = this.form.username;
337        }
338        if (this.$v.form.privilege.$dirty) {
339          userData.privilege = this.form.privilege;
340        }
341        if (this.$v.form.password.$dirty) {
342          userData.password = this.form.password;
343        }
344        if (this.$v.form.manualUnlock.$dirty) {
345          // If form manualUnlock control $dirty then
346          // set user Locked property to false
347          userData.locked = false;
348        }
349        if (Object.entries(userData).length === 1) {
350          this.closeModal();
351          return;
352        }
353      }
354
355      this.$emit('ok', { isNewUser: this.newUser, userData });
356      this.closeModal();
357    },
358    closeModal() {
359      this.$nextTick(() => {
360        this.$refs.modal.hide();
361      });
362    },
363    resetForm() {
364      this.form.originalUsername = '';
365      this.form.status = true;
366      this.form.username = '';
367      this.form.privilege = null;
368      this.form.password = '';
369      this.form.passwordConfirmation = '';
370      this.$v.$reset();
371      this.$emit('hidden');
372    },
373    requirePassword() {
374      if (this.newUser) return true;
375      if (this.$v.form.password.$dirty) return true;
376      if (this.$v.form.passwordConfirmation.$dirty) return true;
377      return false;
378    },
379    onOk(bvModalEvt) {
380      // prevent modal close
381      bvModalEvt.preventDefault();
382      this.handleSubmit();
383    },
384  },
385};
386</script>
387