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