From 676f2fcaac7b81bc3a77c2ecc047d508927814d5 Mon Sep 17 00:00:00 2001
From: Derick Montague <derick.montague@ibm.com>
Date: Mon, 23 Dec 2019 20:53:49 -0600
Subject: Add login form validation

- Sending incorrect credentials returns a 401 and we don't want the page
to redirect if we are trying to login. Wrapped the redirect in an if
block.
- Returning a promise used by the logout  action, which is needed
when not redirecting the page.  Didn't add to the if block since
other errors that use the router to redirect will need the Promise
returned also, e.g. 403.

Signed-off-by: Derick Montague <derick.montague@ibm.com>
Change-Id: I6db706ef7c71ed13baed95dc4264e6ae11d13ad3
---
 src/views/Login/Login.vue | 186 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 134 insertions(+), 52 deletions(-)

(limited to 'src/views/Login/Login.vue')

diff --git a/src/views/Login/Login.vue b/src/views/Login/Login.vue
index b6adf9eb..706d3ecc 100644
--- a/src/views/Login/Login.vue
+++ b/src/views/Login/Login.vue
@@ -1,67 +1,125 @@
 <template>
-  <b-container class="login-container" fluid>
-    <b-row class="login-row" align-v="center">
-      <b-col class="login-branding mt-5 mb-5" md="6">
-        <div class="login-branding__container">
-          <img
-            class="logo"
-            width="200px"
-            src="@/assets/images/openbmc-logo.svg"
-            alt=""
-          />
-          <h1>OpenBMC</h1>
-        </div>
-      </b-col>
-
-      <b-col class="login-form" md="6">
-        <b-form @submit.prevent="login">
-          <b-form-group
-            id="username-group"
-            label="Username"
-            label-for="username"
-          >
-            <b-form-input id="username" v-model="username" type="text" required>
-            </b-form-input>
-          </b-form-group>
-
-          <b-form-group
-            id="password-group"
-            label="Password"
-            label-for="password"
-          >
-            <b-form-input
-              id="password"
-              v-model="password"
-              type="password"
-              required
+  <main>
+    <b-container class="login-container" fluid>
+      <b-row class="login-row" align-v="center">
+        <b-col class="login-branding mt-5 mb-5" md="6">
+          <div class="login-branding__container">
+            <img
+              class="logo"
+              width="200px"
+              src="@/assets/images/openbmc-logo.svg"
+              alt=""
+            />
+            <h1>OpenBMC</h1>
+          </div>
+        </b-col>
+
+        <b-col md="6">
+          <b-form class="login-form" @submit.prevent="login" novalidate>
+            <b-alert
+              class="login-error"
+              v-if="authStatus == 'error'"
+              show
+              variant="danger"
+            >
+              <p id="login-error-alert">
+                <strong>{{ errorMsg.title }}</strong>
+                <span v-if="errorMsg.action">{{ errorMsg.action }}</span>
+              </p>
+            </b-alert>
+            <div class="login-form__section">
+              <label for="username">Username</label>
+              <b-form-input
+                id="username"
+                v-model="userInfo.username"
+                :aria-describedby="
+                  authStatus == 'error' ? 'login-error-alert' : ''
+                "
+                type="text"
+                required
+                autofocus="autofocus"
+              >
+              </b-form-input>
+            </div>
+
+            <div class="login-form__section">
+              <label for="password">Password</label>
+              <b-form-input
+                id="password"
+                v-model="userInfo.password"
+                :aria-describedby="
+                  authStatus == 'error' ? 'login-error-alert' : ''
+                "
+                type="password"
+                required
+              >
+              </b-form-input>
+            </div>
+
+            <b-button
+              type="submit"
+              variant="primary"
+              :disabled="authStatus == 'processing'"
+              >Log in</b-button
             >
-            </b-form-input>
-          </b-form-group>
-
-          <b-button type="submit" variant="primary">Login</b-button>
-        </b-form>
-      </b-col>
-    </b-row>
-  </b-container>
+          </b-form>
+        </b-col>
+      </b-row>
+    </b-container>
+  </main>
 </template>
 
 <script>
 export default {
   name: 'Login',
+  computed: {
+    authStatus() {
+      return this.$store.getters['authentication/authStatus'];
+    }
+  },
   data() {
     return {
-      username: '',
-      password: ''
+      errorMsg: {
+        title: null,
+        action: null
+      },
+      userInfo: {
+        username: null,
+        password: null
+      },
+      disableSubmitButton: false
     };
   },
   methods: {
+    resetState: function() {
+      this.errorMsg.title = null;
+      this.errorMsg.action = null;
+      this.$store.commit('authentication/authReset');
+    },
+    validateRequiredFields: function() {
+      if (!this.userInfo.username || !this.userInfo.password) {
+        this.$store.commit('authentication/authError');
+      }
+    },
     login: function() {
-      const username = this.username;
-      const password = this.password;
-      this.$store
-        .dispatch('authentication/login', [username, password])
-        .then(() => this.$router.push('/'))
-        .catch(error => console.log(error));
+      this.resetState();
+      this.validateRequiredFields();
+      if (this.authStatus !== 'error') {
+        const username = this.userInfo.username;
+        const password = this.userInfo.password;
+        this.$store
+          .dispatch('authentication/login', [username, password])
+          .then(() => {
+            this.$router.push('/');
+          })
+          .catch(error => {
+            this.errorMsg.title = 'Invalid username or password.';
+            this.errorMsg.action = 'Please try again.';
+            console.log(error);
+          });
+      } else {
+        this.errorMsg.title = 'Username and password required.';
+      }
     }
   }
 };
@@ -95,7 +153,7 @@ export default {
   }
 }
 
-.login-form form {
+.login-form {
   max-width: 360px;
   margin-right: auto;
   margin-left: auto;
@@ -105,6 +163,30 @@ export default {
   }
 }
 
+.login-form__section {
+  margin-bottom: $spacer;
+}
+
+.login-error {
+  margin-bottom: $spacer * 2;
+
+  p {
+    margin-bottom: 0;
+  }
+
+  strong {
+    display: block;
+    font-size: 1rem;
+    font-weight: 600;
+    margin-bottom: 0;
+  }
+
+  strong + span {
+    margin-top: $spacer / 2;
+    margin-bottom: 0;
+  }
+}
+
 .login-branding {
   text-align: center;
 }
-- 
cgit v1.2.3

