summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/App.vue31
-rw-r--r--src/assets/images/openbmc-logo.svg1
-rw-r--r--src/assets/styles/_obmc-custom.scss13
-rw-r--r--src/components/AppHeader/AppHeader.vue9
-rw-r--r--src/layouts/AppLayout.vue38
-rw-r--r--src/main.js7
-rw-r--r--src/router/index.js47
-rw-r--r--src/store/api.js15
-rw-r--r--src/store/index.js4
-rw-r--r--src/store/modules/Authentication/AuthenticanStore.js56
-rw-r--r--src/views/Login/Login.vue111
-rw-r--r--src/views/Login/index.js2
12 files changed, 265 insertions, 69 deletions
diff --git a/src/App.vue b/src/App.vue
index d0e1a0ae..56674955 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,18 +1,6 @@
<template>
<div id="app">
- <AppHeader />
- <b-container fluid class="page-container">
- <b-row no-gutters>
- <b-col tag="nav" cols="12" md="3" lg="2">
- <AppNavigation />
- </b-col>
- <b-col cols="12" md="9" lg="10">
- <main id="#main-content">
- <router-view />
- </main>
- </b-col>
- </b-row>
- </b-container>
+ <router-view />
</div>
</template>
@@ -21,22 +9,7 @@
</style>
<script>
-import AppHeader from "@/components/AppHeader";
-import AppNavigation from "@/components/AppNavigation";
export default {
- name: "App",
- components: {
- AppHeader,
- AppNavigation
- }
+ name: "App"
};
</script>
-
-<style lang="scss" scoped>
-.page-container {
- margin-right: 0;
- margin-left: 0;
- padding-right: 0;
- padding-left: 0;
-}
-</style>
diff --git a/src/assets/images/openbmc-logo.svg b/src/assets/images/openbmc-logo.svg
new file mode 100644
index 00000000..d0fa158c
--- /dev/null
+++ b/src/assets/images/openbmc-logo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 241.23 240.05"><defs><style>.cls-1{fill:#a6a8ab;}.cls-2{fill:url(#linear-gradient);}.cls-3{fill:url(#linear-gradient-2);}.cls-4{fill:url(#linear-gradient-3);}.cls-5{fill:url(#linear-gradient-4);}.cls-6{fill:#626366;}</style><linearGradient id="linear-gradient" x1="82.9" y1="11.55" x2="82.9" y2="154.54" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00b0da"/><stop offset="1" stop-color="#008abf"/></linearGradient><linearGradient id="linear-gradient-2" x1="81.55" y1="27.55" x2="81.55" y2="158.66" xlink:href="#linear-gradient"/><linearGradient id="linear-gradient-3" x1="156.66" y1="51.54" x2="156.66" y2="154.8" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#a5d440"/><stop offset="1" stop-color="#8cce3f"/></linearGradient><linearGradient id="linear-gradient-4" x1="158.41" y1="51.54" x2="158.41" y2="154.8" xlink:href="#linear-gradient-3"/></defs><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M241.23,205.77a18.66,18.66,0,1,0-.24,16L237.26,220a14.51,14.51,0,1,1,.21-12.46Z"/><path class="cls-2" d="M65.85,81.86a53.68,53.68,0,0,0,11.61,33.41c-.1.29-.15.6-.22.9a10.81,10.81,0,0,0-.34,2.57,11,11,0,1,0,11-11,10.75,10.75,0,0,0-1.2.07c-.31,0-.61.08-.91.13A42.82,42.82,0,0,1,99.95,43.86h0V2.07l-.77.21q-3.63.94-7.12,2.2c-1.29.47-2.58,1-3.84,1.48h0V38.19l-.13.1A53.79,53.79,0,0,0,65.85,81.86Z"/><path class="cls-3" d="M120.28,96.58a14.54,14.54,0,0,1-14.55-14.37H93.59v0a26.29,26.29,0,0,0,21,25.65v45.35A71.13,71.13,0,0,1,63.9,38.1c.31.06.63.1,1,.13s.64,0,1,0a10.83,10.83,0,1,0-10.25-7.41,82.23,82.23,0,0,0,64.18,133.6c1.41,0,2.81-.06,4.2-.14l1.63-.09h0V95.57A14.47,14.47,0,0,1,120.28,96.58Z"/><path class="cls-4" d="M171.95,68.54a53.78,53.78,0,0,0-9.85-19.71,11.31,11.31,0,0,0,.32-1.3,10.78,10.78,0,0,0,.24-2.17,11,11,0,1,0-8.89,10.8,42.83,42.83,0,0,1-14.17,64.08V162c1.08-.27,2.14-.56,3.2-.87a82.35,82.35,0,0,0,8.53-3V125.91a53.91,53.91,0,0,0,20.6-57.37Z"/><path class="cls-5" d="M184.63,132.75A82.21,82.21,0,0,0,119.79,0c-1.64,0-3.26.06-4.87.16h-.11V68.55h0A14.53,14.53,0,0,1,120,67.48h.27A14.56,14.56,0,0,1,134.87,82s0,.07,0,.11,0,.08,0,.13h11.08A26.21,26.21,0,0,0,125.81,56.8V11.3A71.14,71.14,0,0,1,176,125.83h-.07a11,11,0,0,0-12.58,10.88,11,11,0,0,0,11,11h0a11,11,0,0,0,10.54-14.13C184.82,133.3,184.73,133,184.63,132.75Z"/><polygon class="cls-1" points="201.22 231.42 201.22 195.65 196.53 195.65 182 225.14 167.47 195.65 162.72 195.65 162.72 231.42 166.89 231.42 166.89 204.21 180.33 231.42 183.67 231.42 197.1 204.21 197.1 231.42 201.22 231.42"/><path class="cls-6" d="M119.82,208.4a10.6,10.6,0,0,0-7.9-3.34,10.15,10.15,0,0,0-4.16.83,15.94,15.94,0,0,0-3.62,2.24v-2.7H99.91v26h4.23V216.64a7.74,7.74,0,0,1,2.08-5.5,7.48,7.48,0,0,1,10.66,0,7.76,7.76,0,0,1,2.08,5.48v14.78h4.11v-15a11.12,11.12,0,0,0-3.24-8"/><path class="cls-6" d="M63.68,224.4a12.41,12.41,0,0,1-4.86,5.17,13.54,13.54,0,0,1-7,1.85H45.54V240h-4V205.43H51.64a13.41,13.41,0,0,1,9.57,3.76,12.73,12.73,0,0,1,2.47,15.21m-18.14,3.24h5.77A9.48,9.48,0,0,0,58.05,225a8.59,8.59,0,0,0,2.76-6.54,8.38,8.38,0,0,0-2.7-6.41,9.43,9.43,0,0,0-6.68-2.51H45.54Z"/><path class="cls-6" d="M96.44,219.75a4.56,4.56,0,0,0,.14-1.36c0-7.38-6.27-13.36-14-13.36s-14,6-14,13.36,6.27,13.36,14,13.36a14,14,0,0,0,11.93-6.52l-3.25-2.45a9.89,9.89,0,0,1-8.68,5,9.43,9.43,0,1,1,0-18.83,9.8,9.8,0,0,1,9.35,6.54c0,.07.15.63.17.7H76.59v3.52Z"/><path class="cls-6" d="M33.19,213.53A14.53,14.53,0,1,1,18.66,199a14.53,14.53,0,0,1,14.53,14.53m4.12,0a18.66,18.66,0,1,0-18.66,18.66,18.66,18.66,0,0,0,18.66-18.66"/><path class="cls-1" d="M154.37,220.69a6.77,6.77,0,0,1-6.75,6.79H132.14V213.9h15.49a6.78,6.78,0,0,1,6.75,6.79m-2.29-15.93a5.08,5.08,0,0,1-5.05,5.08l-14.89,0V199.67H147a5.07,5.07,0,0,1,5.05,5.08m.94,6.7a9,9,0,0,0-5.69-15.75H128v35.75h20.14l.28,0v0A10.73,10.73,0,0,0,153,211.46"/></g></g></svg> \ No newline at end of file
diff --git a/src/assets/styles/_obmc-custom.scss b/src/assets/styles/_obmc-custom.scss
index 45dec1f8..7deb6cba 100644
--- a/src/assets/styles/_obmc-custom.scss
+++ b/src/assets/styles/_obmc-custom.scss
@@ -1,16 +1,3 @@
-// Major breakpoints using 16px base em * 16
-// xs: 0, sm: 768px, md: 1024px, lg: 1376px, xl: 1600px
-// Using em's will allow for changes if base font size is updated
-// to accommodate for responsive text. Using 0 as xs due to
-// Bootstrap requirements.
-$grid-breakpoints: (
- xs: 0,
- sm: 48em,
- md: 64em,
- lg: 86em,
- xl: 100em
-);
-
// Required
@import "~bootstrap/scss/functions";
@import "./functions";
diff --git a/src/components/AppHeader/AppHeader.vue b/src/components/AppHeader/AppHeader.vue
index 9177403c..8dacd03d 100644
--- a/src/components/AppHeader/AppHeader.vue
+++ b/src/components/AppHeader/AppHeader.vue
@@ -7,9 +7,9 @@
<b-nav-text>BMC System Management</b-nav-text>
</b-navbar-nav>
<b-navbar-nav small class="ml-auto">
- <b-nav-item>
+ <b-nav-item @click="logout">
<user-avatar-20 />
- User Avatar
+ Logout
</b-nav-item>
</b-navbar-nav>
</b-navbar>
@@ -77,6 +77,11 @@ export default {
methods: {
getHostInfo() {
this.$store.dispatch("global/getHostName");
+ },
+ logout() {
+ this.$store.dispatch("authentication/logout").then(() => {
+ this.$router.push("/login");
+ });
}
}
};
diff --git a/src/layouts/AppLayout.vue b/src/layouts/AppLayout.vue
new file mode 100644
index 00000000..dcfb52e7
--- /dev/null
+++ b/src/layouts/AppLayout.vue
@@ -0,0 +1,38 @@
+<template>
+ <div>
+ <AppHeader />
+ <b-container fluid class="page-container">
+ <b-row no-gutters>
+ <b-col tag="nav" cols="12" md="3" lg="2">
+ <AppNavigation />
+ </b-col>
+ <b-col cols="12" md="9" lg="10">
+ <main id="#main-content">
+ <router-view />
+ </main>
+ </b-col>
+ </b-row>
+ </b-container>
+ </div>
+</template>
+
+<script>
+import AppHeader from "@/components/AppHeader";
+import AppNavigation from "@/components/AppNavigation";
+export default {
+ name: "App",
+ components: {
+ AppHeader,
+ AppNavigation
+ }
+};
+</script>
+
+<style lang="scss" scoped>
+.page-container {
+ margin-right: 0;
+ margin-left: 0;
+ padding-right: 0;
+ padding-left: 0;
+}
+</style>
diff --git a/src/main.js b/src/main.js
index e18ce89e..6f4acc6d 100644
--- a/src/main.js
+++ b/src/main.js
@@ -7,7 +7,10 @@ import {
BadgePlugin,
ButtonPlugin,
CollapsePlugin,
+ FormPlugin,
FormCheckboxPlugin,
+ FormGroupPlugin,
+ FormInputPlugin,
LayoutPlugin,
LinkPlugin,
ListGroupPlugin,
@@ -22,7 +25,11 @@ Vue.filter("date", dateFilter);
Vue.use(BadgePlugin);
Vue.use(ButtonPlugin);
Vue.use(CollapsePlugin);
+Vue.use(FormPlugin);
Vue.use(FormCheckboxPlugin);
+Vue.use(FormGroupPlugin);
+Vue.use(FormInputPlugin);
+Vue.use(LayoutPlugin);
Vue.use(LayoutPlugin);
Vue.use(LinkPlugin);
Vue.use(ListGroupPlugin);
diff --git a/src/router/index.js b/src/router/index.js
index 11d1a475..698aa700 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,28 +1,35 @@
import Vue from "vue";
import VueRouter from "vue-router";
+import store from "../store/index";
+import AppLayout from "../layouts/AppLayout.vue";
Vue.use(VueRouter);
const routes = [
{
path: "/",
- name: "overview",
- component: () => import("@/views/Overview")
+ name: "",
+ meta: {
+ requiresAuth: true
+ },
+ component: AppLayout,
+ children: [
+ {
+ path: "",
+ component: () => import("@/views/Overview")
+ },
+ {
+ path: "/access-control/local-user-management",
+ name: "local-users",
+ component: () => import("@/views/AccessControl/LocalUserManagement")
+ }
+ ]
},
{
- path: "/access-control/local-user-management",
- name: "local-users",
- component: () => import("@/views/AccessControl/LocalUserManagement")
+ path: "/login",
+ name: "login",
+ component: () => import("@/views/Login")
}
- // {
- // path: "/about",
- // name: "about",
- // // route level code-splitting
- // // this generates a separate chunk (about.[hash].js) for this route
- // // which is lazy-loaded when the route is visited.
- // component: () =>
- // import(/* webpackChunkName: "about" */ "../views/About.vue")
- // }
];
const router = new VueRouter({
@@ -32,4 +39,16 @@ const router = new VueRouter({
linkExactActiveClass: "nav__link--current"
});
+router.beforeEach((to, from, next) => {
+ if (to.matched.some(record => record.meta.requiresAuth)) {
+ if (store.getters["authentication/isLoggedIn"]) {
+ next();
+ return;
+ }
+ next("/login");
+ } else {
+ next();
+ }
+});
+
export default router;
diff --git a/src/store/api.js b/src/store/api.js
index d40ad0ad..39a6355c 100644
--- a/src/store/api.js
+++ b/src/store/api.js
@@ -2,15 +2,9 @@ import Axios from "axios";
const api = Axios.create();
-// TODO: this is a temporary workaround until
-// authentication with login is working
-const username = process.env.VUE_APP_USERNAME;
-const password = process.env.VUE_APP_PASSWORD;
-if (username && password) {
- api.defaults.auth = {};
- api.defaults.auth.username = username;
- api.defaults.auth.password = password;
-}
+// TODO: Permanent authentication solution
+// Using defaults to set auth for sending
+// auth object in header
export default {
get(path) {
@@ -30,5 +24,6 @@ export default {
},
all(promises) {
return Axios.all(promises);
- }
+ },
+ defaults: api.defaults
};
diff --git a/src/store/index.js b/src/store/index.js
index af06a471..b4be6ccd 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -1,8 +1,9 @@
import Vue from "vue";
import Vuex from "vuex";
-import LocalUserManagementStore from "./modules/AccessControl/LocalUserMangementStore";
import GlobalStore from "./modules/GlobalStore";
+import AuthenticationStore from "./modules/Authentication/AuthenticanStore";
+import LocalUserManagementStore from "./modules/AccessControl/LocalUserMangementStore";
Vue.use(Vuex);
@@ -12,6 +13,7 @@ export default new Vuex.Store({
actions: {},
modules: {
global: GlobalStore,
+ authentication: AuthenticationStore,
localUsers: LocalUserManagementStore
}
});
diff --git a/src/store/modules/Authentication/AuthenticanStore.js b/src/store/modules/Authentication/AuthenticanStore.js
new file mode 100644
index 00000000..828c3cc8
--- /dev/null
+++ b/src/store/modules/Authentication/AuthenticanStore.js
@@ -0,0 +1,56 @@
+import api from "../../api";
+
+const AuthenticationStore = {
+ namespaced: true,
+ state: {
+ auth: {},
+ status: "",
+ token: sessionStorage.getItem("token") || ""
+ },
+ getters: {
+ authStatus: state => state.status,
+ isLoggedIn: state => !!state.token
+ },
+ mutations: {
+ authRequest(state) {
+ state.status = "loading";
+ },
+ authSuccess(state, token, auth) {
+ state.status = "authenicated";
+ state.auth = auth;
+ state.token = token;
+ },
+ authError(state) {
+ state.status = "error";
+ },
+ logout(state) {
+ state.status = "";
+ state.token = "";
+ }
+ },
+ actions: {
+ login({ commit }, auth) {
+ commit("authRequest");
+ return api
+ .post("/login", auth)
+ .then(response => {
+ const token = response.data.token;
+ sessionStorage.setItem("token", token);
+ api.defaults.auth = auth; // TODO Permanent Solution
+ commit("authSuccess", token, auth);
+ })
+ .catch(error => {
+ commit("authError");
+ sessionStorage.removeItem("token");
+ throw new Error(error);
+ });
+ },
+ logout({ commit }) {
+ commit("logout");
+ sessionStorage.removeItem("token");
+ api.defaults.auth = {}; // Permanent solution
+ }
+ }
+};
+
+export default AuthenticationStore;
diff --git a/src/views/Login/Login.vue b/src/views/Login/Login.vue
new file mode 100644
index 00000000..f122d289
--- /dev/null
+++ b/src/views/Login/Login.vue
@@ -0,0 +1,111 @@
+<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
+ >
+ </b-form-input>
+ </b-form-group>
+
+ <b-button type="submit" variant="primary">Login</b-button>
+ </b-form>
+ </b-col>
+ </b-row>
+ </b-container>
+</template>
+
+<script>
+export default {
+ name: "Login",
+ data() {
+ return {
+ username: "",
+ password: ""
+ };
+ },
+ methods: {
+ 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));
+ }
+ }
+};
+</script>
+
+<style lang="scss" scoped>
+@import "~bootstrap/scss/functions";
+@import "~bootstrap/scss/variables";
+@import "~bootstrap/scss/mixins";
+
+.login-container {
+ @include media-breakpoint-up(md) {
+ background: linear-gradient(
+ to right,
+ var(--light) 50%,
+ var(--secondary-light) 50%
+ );
+ }
+}
+
+.login-row {
+ @include media-breakpoint-up(md) {
+ min-height: 100vh;
+ }
+}
+
+.login-branding__container {
+ @include media-breakpoint-up(md) {
+ float: right;
+ margin-right: 4rem;
+ }
+}
+
+.login-form form {
+ max-width: 360px;
+ margin-right: auto;
+ margin-left: auto;
+
+ @include media-breakpoint-up(md) {
+ margin-left: 4rem;
+ }
+}
+
+.login-branding {
+ text-align: center;
+}
+</style>
diff --git a/src/views/Login/index.js b/src/views/Login/index.js
new file mode 100644
index 00000000..7fc2b5ca
--- /dev/null
+++ b/src/views/Login/index.js
@@ -0,0 +1,2 @@
+import Login from "./Login.vue";
+export default Login;