From 05b347af6cd556733eddda3c5cf2b0c2a0060903 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20G=C3=B6bel?= <dgoebel@techfak.uni-bielefeld.de>
Date: Thu, 21 Jul 2022 14:13:55 +0200
Subject: [PATCH] Add login page and basic nested router with nav guards

#2
---
 src/App.vue                    |  35 ++++++++++++++++++-------
 src/assets/images/ls-login.png | Bin 0 -> 8560 bytes
 src/assets/logo.svg            |   1 -
 src/components/NavbarTop.vue   |  35 +++++++++++++++++++------
 src/components/SidebarLeft.vue |  23 ++++++++---------
 src/router/index.ts            |  32 ++++++++++++++++-------
 src/stores/auth.ts             |  45 +++++++++++++++++++++++++++++++++
 src/views/BucketsView.vue      |  20 +--------------
 src/views/DashboardView.vue    |  10 ++++++++
 src/views/LoginView.vue        |  34 +++++++++++++++++++++++++
 10 files changed, 176 insertions(+), 59 deletions(-)
 create mode 100644 src/assets/images/ls-login.png
 delete mode 100644 src/assets/logo.svg
 create mode 100644 src/stores/auth.ts
 create mode 100644 src/views/DashboardView.vue
 create mode 100644 src/views/LoginView.vue

diff --git a/src/App.vue b/src/App.vue
index 420561b..e293b27 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,16 +1,33 @@
 <script setup lang="ts">
 import NavbarTop from "./components/NavbarTop.vue";
-import SidebarLeft from "./components/SidebarLeft.vue";
+import { onBeforeMount } from "vue";
+import { useCookies } from "vue3-cookies";
+import { useAuthStore } from "@/stores/auth";
+import { useRouter } from "vue-router";
+
+const { cookies } = useCookies();
+const store = useAuthStore();
+const router = useRouter();
+
+onBeforeMount(() => {
+  store.setToken(cookies.get("bearer"));
+  router.beforeEach(async (to) => {
+    // make sure the user is authenticated
+    if (
+      !store.authenticated &&
+      // ❗️ Avoid an infinite redirect
+      to.name !== "login"
+    ) {
+      // redirect the user to the login page
+      return { name: "login" };
+    }
+  });
+});
 </script>
 
-<template style="height: 100%">
+<template>
   <NavbarTop />
-  <SidebarLeft />
-  <router-view class="cona"></router-view>
+  <router-view></router-view>
 </template>
 
-<style scoped>
-.cona {
-  min-height: 80vh;
-}
-</style>
+<style scoped></style>
diff --git a/src/assets/images/ls-login.png b/src/assets/images/ls-login.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab72fa293def5199720b4a00bac0d824aad6d26a
GIT binary patch
literal 8560
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
OPEJ}`sz%~l$o~OMm(fuG

literal 0
HcmV?d00001

diff --git a/src/assets/logo.svg b/src/assets/logo.svg
deleted file mode 100644
index bc826fe..0000000
--- a/src/assets/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"  xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
\ No newline at end of file
diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue
index edf18f4..07ad562 100644
--- a/src/components/NavbarTop.vue
+++ b/src/components/NavbarTop.vue
@@ -2,8 +2,15 @@
 import BootstrapIcon from "./BootstrapIcon.vue";
 import { reactive, onBeforeUnmount, onMounted } from "vue";
 import { MiscellaneousService } from "@/client";
+import { useAuthStore } from "@/stores/auth";
+import { useRouter } from "vue-router";
+import { useCookies } from "vue3-cookies";
 
-const api_connection = reactive({ connected: false, timer: null });
+const router = useRouter();
+const store = useAuthStore();
+const { cookies } = useCookies();
+
+const api_connection = reactive({ connected: true, timer: null });
 let timer: ReturnType<typeof setInterval> | undefined = undefined;
 function checkApiHealth() {
   MiscellaneousService.miscellaneousHealthCheck()
@@ -15,6 +22,12 @@ function checkApiHealth() {
     });
 }
 
+function logout() {
+  store.logout();
+  cookies.remove("bearer");
+  router.push({ name: "login" });
+}
+
 onMounted(() => {
   checkApiHealth();
   timer = setInterval(checkApiHealth, 10000);
@@ -36,7 +49,10 @@ onBeforeUnmount(() => {
       >
         Backend not reachable
       </span>
-      <div class="dropdown d-flex me-3">
+      <div
+        class="dropdown d-flex me-3"
+        v-if="store.authenticated && store.user != null"
+      >
         <a
           href="#"
           class="d-flex align-items-center text-white text-decoration-none dropdown-toggle-split"
@@ -44,7 +60,7 @@ onBeforeUnmount(() => {
           data-bs-toggle="dropdown"
           aria-expanded="false"
         >
-          <strong class="me-2">Bilbo Baggins</strong>
+          <strong class="me-2">{{ store.user.display_name }}</strong>
           <bootstrap-icon
             icon="person-circle"
             fill="white"
@@ -56,15 +72,18 @@ onBeforeUnmount(() => {
           class="dropdown-menu dropdown-menu-dark text-small shadow"
           aria-labelledby="dropdownUser1"
         >
-          <li><a class="dropdown-item" href="#">New project...</a></li>
-          <li><a class="dropdown-item" href="#">Settings</a></li>
-          <li><a class="dropdown-item" href="#">Profile</a></li>
           <li><hr class="dropdown-divider" /></li>
-          <li><a class="dropdown-item" href="#">Sign out</a></li>
+          <li>
+            <a class="dropdown-item pseudo-link" @click="logout">Sign out</a>
+          </li>
         </ul>
       </div>
     </div>
   </nav>
 </template>
 
-<style scoped></style>
+<style scoped>
+.pseudo-link {
+  cursor: pointer;
+}
+</style>
diff --git a/src/components/SidebarLeft.vue b/src/components/SidebarLeft.vue
index a5d1866..677c417 100644
--- a/src/components/SidebarLeft.vue
+++ b/src/components/SidebarLeft.vue
@@ -2,32 +2,28 @@
 
 <template>
   <div
-    class="d-flex flex-column flex-shrink-0 p-3 text-white bg-dark"
-    style="width: 200px"
+    class="d-flex flex-column flex-shrink-0 p-3 text-white bg-dark position-fixed top-50 start-0 translate-middle-y"
+    style="width: 200px; min-height: 80vh"
   >
     <ul class="nav nav-pills flex-column mb-auto">
-      <li class="nav-item">
+      <li class="nav-item mb-1">
         <button
-          class="btn btn-toggle align-items-center rounded collapsed text-white"
+          class="btn btn-lg btn-toggle align-items-center rounded collapsed text-white"
           data-bs-toggle="collapse"
-          data-bs-target="#home-collapse"
+          data-bs-target="#os-collapse"
           aria-expanded="true"
         >
           Object Storage
         </button>
-        <div class="collapse show" id="home-collapse">
+        <div class="collapse show fs-5" id="os-collapse">
           <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
             <li>
-              <router-link
-                to="/object-storage/buckets"
-                class="link-light rounded"
+              <router-link :to="{ name: 'buckets' }" class="link-light rounded"
                 >Buckets</router-link
               >
             </li>
             <li>
-              <router-link
-                to="/object-storage/s3-keys"
-                class="link-light rounded"
+              <router-link :to="{ name: 's3_keys' }" class="link-light rounded"
                 >S3 Keys</router-link
               >
             </li>
@@ -38,4 +34,5 @@
   </div>
 </template>
 
-<style scoped></style>
+<style scoped>
+</style>
diff --git a/src/router/index.ts b/src/router/index.ts
index 3cc545e..6034817 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,21 +1,35 @@
 import { createRouter, createWebHistory } from "vue-router";
 import BucketsView from "../views/BucketsView.vue";
+import DashboardView from "../views/DashboardView.vue";
+import LoginView from "../views/LoginView.vue";
 
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
   routes: [
     {
-      path: "/object-storage/buckets",
-      name: "buckets",
-      component: BucketsView,
+      path: "/dashboard",
+      name: "dashboard",
+      component: DashboardView,
+      children: [
+        {
+          path: "object-storage/buckets",
+          name: "buckets",
+          component: BucketsView,
+        },
+        {
+          path: "object-storage/s3-keys",
+          name: "s3_keys",
+          // 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("../views/S3KeysView.vue"),
+        },
+      ],
     },
     {
-      path: "/object-storage/s3-keys",
-      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("../views/S3KeysView.vue"),
+      path: "/login",
+      name: "login",
+      component: LoginView,
     },
     {
       path: "/",
diff --git a/src/stores/auth.ts b/src/stores/auth.ts
new file mode 100644
index 0000000..93dd60e
--- /dev/null
+++ b/src/stores/auth.ts
@@ -0,0 +1,45 @@
+import { defineStore } from "pinia";
+import type { User } from "@/client";
+import { UserService } from "@/client";
+import { OpenAPI } from "@/client";
+
+export type RootState = {
+  token: string | null;
+  user: User | null;
+};
+
+export const useAuthStore = defineStore({
+  id: "auth",
+  state: () =>
+    ({
+      token: null,
+      user: null,
+    } as RootState),
+  getters: {
+    authenticated: (state) => state.token != null,
+  },
+  actions: {
+    setToken(token: string | null) {
+      if (token != null) {
+        OpenAPI.TOKEN = token;
+        this.token = token;
+        UserService.userGetLoggedInUser()
+          .then((user) => {
+            this.user = user;
+          })
+          .catch(() => {
+            this.token = null;
+          });
+      } else {
+        this.token = null;
+        this.user = null;
+      }
+    },
+    updateUser() {
+      this.setToken(this.token);
+    },
+    logout() {
+      this.setToken(null);
+    },
+  },
+});
diff --git a/src/views/BucketsView.vue b/src/views/BucketsView.vue
index 94874bb..3635de2 100644
--- a/src/views/BucketsView.vue
+++ b/src/views/BucketsView.vue
@@ -1,25 +1,7 @@
-<script setup lang="ts">
-import { onMounted, reactive } from "vue";
-import { useCookies } from "vue3-cookies";
-
-const { cookies } = useCookies();
-const my_cookie = reactive({
-  val: "defaults",
-});
-
-onMounted(() => {
-  if (cookies.isKey("bearer")) {
-    my_cookie.val = cookies.get("bearer");
-  }
-});
-</script>
+<script setup lang="ts"></script>
 
 <template>
   <main>
-    <p v-if="my_cookie.val !== 'defaults'">
-      Cookie Present: {{ my_cookie.val }}
-    </p>
-    <p v-else>No Cookie set</p>
     <div>This the is the Buckets Page</div>
   </main>
 </template>
diff --git a/src/views/DashboardView.vue b/src/views/DashboardView.vue
new file mode 100644
index 0000000..ed97b6c
--- /dev/null
+++ b/src/views/DashboardView.vue
@@ -0,0 +1,10 @@
+<script setup lang="ts">
+import SidebarLeft from "../components/SidebarLeft.vue";
+</script>
+
+<template>
+  <SidebarLeft />
+  <router-view></router-view>
+</template>
+
+<style scoped></style>
diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue
new file mode 100644
index 0000000..5de83eb
--- /dev/null
+++ b/src/views/LoginView.vue
@@ -0,0 +1,34 @@
+<script setup lang="ts">
+import { onBeforeMount } from "vue";
+import { useAuthStore } from "@/stores/auth";
+import { useRouter } from "vue-router";
+import { OpenAPI } from "@/client";
+
+const router = useRouter();
+
+const store = useAuthStore();
+
+onBeforeMount(() => {
+  if (store.authenticated) {
+    // If user is authenticated redirect him to the dashboard
+    router.push({ name: "buckets" });
+  }
+});
+</script>
+
+<template>
+  <div
+    class="card text-center bg-dark ms-md-auto position-absolute top-50 left-50 translate-middle"
+  >
+    <div class="card-header text-dark bg-light">LoginView</div>
+    <div class="card-body p-5">
+      <h5 class="card-title">Login</h5>
+      <p class="card-text">Login to this service with LifeScience</p>
+      <a :href="OpenAPI.BASE + '/auth/login'" class="m-2">
+        <img src="/src/assets/images/ls-login.png" alt="[LS Login]" />
+      </a>
+    </div>
+  </div>
+</template>
+
+<style scoped></style>
-- 
GitLab