summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerick Montague <derick.montague@ibm.com>2019-12-24 05:53:49 +0300
committerYoshie Muranaka <yoshiemuranaka@gmail.com>2020-01-29 01:18:05 +0300
commit676f2fcaac7b81bc3a77c2ecc047d508927814d5 (patch)
tree61268fd6178f6a86bc4d51e53217569ec628204e
parent126eaabea5edf8df4ed0dfb9c7af4ea246a5628a (diff)
downloadwebui-vue-676f2fcaac7b81bc3a77c2ecc047d508927814d5.tar.xz
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
-rw-r--r--src/main.js2
-rw-r--r--src/store/api.js6
-rw-r--r--src/store/modules/Authentication/AuthenticanStore.js5
-rw-r--r--src/views/Login/Login.vue186
4 files changed, 145 insertions, 54 deletions
diff --git a/src/main.js b/src/main.js
index 023c24ec..b69c6591 100644
--- a/src/main.js
+++ b/src/main.js
@@ -4,6 +4,7 @@ import router from './router';
import store from './store';
import { dateFilter } from 'vue-date-fns';
import {
+ AlertPlugin,
BadgePlugin,
ButtonPlugin,
CollapsePlugin,
@@ -24,6 +25,7 @@ import {
Vue.filter('date', dateFilter);
+Vue.use(AlertPlugin);
Vue.use(BadgePlugin);
Vue.use(ButtonPlugin);
Vue.use(CollapsePlugin);
diff --git a/src/store/api.js b/src/store/api.js
index 4918d804..0f8c9484 100644
--- a/src/store/api.js
+++ b/src/store/api.js
@@ -10,12 +10,16 @@ api.interceptors.response.use(undefined, error => {
// TODO: Provide user with a notification and way to keep system active
if (response.status == 401) {
- window.location = '/login';
+ if (response.config.url != '/login') {
+ window.location = '/login';
+ }
}
if (response.status == 403) {
router.push({ name: 'unauthorized' });
}
+
+ return Promise.reject(error);
});
export default {
diff --git a/src/store/modules/Authentication/AuthenticanStore.js b/src/store/modules/Authentication/AuthenticanStore.js
index 88456e95..3a554b6b 100644
--- a/src/store/modules/Authentication/AuthenticanStore.js
+++ b/src/store/modules/Authentication/AuthenticanStore.js
@@ -13,7 +13,7 @@ const AuthenticationStore = {
},
mutations: {
authRequest(state) {
- state.status = 'loading';
+ state.status = 'processing';
},
authSuccess(state) {
state.status = 'authenticated';
@@ -22,6 +22,9 @@ const AuthenticationStore = {
authError(state) {
state.status = 'error';
},
+ authReset(state) {
+ state.status = '';
+ },
logout(state) {
state.status = '';
Cookies.remove('XSRF-TOKEN');
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;
}