Add account settings to local user page

Adds ability to change account LockoutThreshold and
LockoutDuration properties from the GUI.

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: Ieeb75aa83c07b3de840bccdfc28e2d6e87512e2e
diff --git a/src/views/AccessControl/LocalUserManagement/ModalSettings.vue b/src/views/AccessControl/LocalUserManagement/ModalSettings.vue
index afe2d95..2e41b29 100644
--- a/src/views/AccessControl/LocalUserManagement/ModalSettings.vue
+++ b/src/views/AccessControl/LocalUserManagement/ModalSettings.vue
@@ -1,14 +1,195 @@
 <template>
-  <b-modal id="modal-settings" title="Account policy settings"> </b-modal>
+  <b-modal
+    id="modal-settings"
+    ref="modal"
+    :title="$t('localUserManagement.accountPolicySettings')"
+    :ok-title="$t('global.actions.save')"
+    @ok="onOk"
+    @hidden="resetForm"
+  >
+    <b-form novalidate @submit="handleSubmit">
+      <b-container>
+        <b-row>
+          <b-col>
+            <b-form-group
+              :label="$t('localUserManagement.modals.maxFailedLoginAttempts')"
+              label-for="lockout-threshold"
+            >
+              <b-form-text id="lockout-threshold-help-block">
+                {{
+                  $t('global.formField.valueMustBeBetween', {
+                    min: 0,
+                    max: 65535
+                  })
+                }}
+              </b-form-text>
+              <b-form-input
+                id="lockout-threshold"
+                v-model.number="form.lockoutThreshold"
+                type="number"
+                aria-describedby="lockout-threshold-help-block"
+                :state="getValidationState($v.form.lockoutThreshold)"
+                @input="$v.form.lockoutThreshold.$touch()"
+              />
+              <b-form-invalid-feedback role="alert">
+                <template v-if="!$v.form.lockoutThreshold.required">
+                  {{ $t('global.formField.fieldRequired') }}
+                </template>
+                <template
+                  v-if="
+                    !$v.form.lockoutThreshold.minLength ||
+                      !$v.form.lockoutThreshold.maxLength
+                  "
+                >
+                  {{
+                    $t('global.formField.valueMustBeBetween', {
+                      min: 0,
+                      max: 65535
+                    })
+                  }}
+                </template>
+              </b-form-invalid-feedback>
+            </b-form-group>
+          </b-col>
+          <b-col>
+            <b-form-group
+              :label="$t('localUserManagement.modals.userUnlockMethod')"
+            >
+              <b-form-radio
+                v-model="form.unlockMethod"
+                name="unlock-method"
+                class="mb-2"
+                :value="0"
+                @input="$v.form.unlockMethod.$touch()"
+              >
+                {{ $t('localUserManagement.modals.manual') }}
+              </b-form-radio>
+              <b-form-radio
+                v-model="form.unlockMethod"
+                name="unlock-method"
+                :value="1"
+                @input="$v.form.unlockMethod.$touch()"
+              >
+                {{ $t('localUserManagement.modals.automaticAfterTimeout') }}
+              </b-form-radio>
+              <div class="mt-3 ml-4">
+                <b-form-text id="lockout-duration-help-block">
+                  {{ $t('localUserManagement.modals.timeoutDurationSeconds') }}
+                </b-form-text>
+                <b-form-input
+                  v-model.number="form.lockoutDuration"
+                  aria-describedby="lockout-duration-help-block"
+                  type="number"
+                  :state="getValidationState($v.form.lockoutDuration)"
+                  :readonly="$v.form.unlockMethod.$model === 0"
+                  @input="$v.form.lockoutDuration.$touch()"
+                />
+                <b-form-invalid-feedback role="alert">
+                  <template v-if="!$v.form.lockoutDuration.required">
+                    {{ $t('global.formField.fieldRequired') }}
+                  </template>
+                  <template v-else-if="!$v.form.lockoutDuration.minvalue">
+                    {{ $t('global.formField.mustBeAtLeast', { value: 1 }) }}
+                  </template>
+                </b-form-invalid-feedback>
+              </div>
+            </b-form-group>
+          </b-col>
+        </b-row>
+      </b-container>
+    </b-form>
+  </b-modal>
 </template>
 
 <script>
+import VuelidateMixin from '../../../components/Mixins/VuelidateMixin.js';
+import {
+  required,
+  requiredIf,
+  minValue,
+  maxValue
+} from 'vuelidate/lib/validators';
+
 export default {
+  mixins: [VuelidateMixin],
   props: {
     settings: {
       type: Object,
       required: true
     }
+  },
+  data() {
+    return {
+      form: {
+        lockoutThreshold: 0,
+        unlockMethod: 0,
+        lockoutDuration: null
+      }
+    };
+  },
+  watch: {
+    settings: function({ lockoutThreshold, lockoutDuration }) {
+      this.form.lockoutThreshold = lockoutThreshold;
+      this.form.unlockMethod = lockoutDuration ? 1 : 0;
+      this.form.lockoutDuration = lockoutDuration ? lockoutDuration : null;
+    }
+  },
+  validations: {
+    form: {
+      lockoutThreshold: {
+        minValue: minValue(0),
+        maxValue: maxValue(65535),
+        required
+      },
+      unlockMethod: { required },
+      lockoutDuration: {
+        minValue: function(value) {
+          return this.form.unlockMethod === 0 || value > 0;
+        },
+        required: requiredIf(function() {
+          return this.form.unlockMethod === 1;
+        })
+      }
+    }
+  },
+  methods: {
+    handleSubmit() {
+      this.$v.$touch();
+      if (this.$v.$invalid) return;
+
+      let lockoutThreshold;
+      let lockoutDuration;
+      if (this.$v.form.lockoutThreshold.$dirty) {
+        lockoutThreshold = this.form.lockoutThreshold;
+      }
+      if (this.$v.form.unlockMethod.$dirty) {
+        lockoutDuration = this.form.unlockMethod
+          ? this.form.lockoutDuration
+          : 0;
+      }
+
+      this.$emit('ok', { lockoutThreshold, lockoutDuration });
+      this.closeModal();
+    },
+    onOk(bvModalEvt) {
+      // prevent modal close
+      bvModalEvt.preventDefault();
+      this.handleSubmit();
+    },
+    closeModal() {
+      this.$nextTick(() => {
+        this.$refs.modal.hide();
+      });
+    },
+    resetForm() {
+      // Reset form models
+      this.form.lockoutThreshold = this.settings.lockoutThreshold;
+      this.form.unlockMethod = this.settings.lockoutDuration ? 1 : 0;
+      this.form.lockoutDuration = this.settings.lockoutDuration
+        ? this.settings.lockoutDuration
+        : null;
+      this.$v.$reset(); // clear validations
+    }
   }
 };
 </script>