From 14965aa60b08758d279fa1ec86acd1dea42ccf20 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Thu, 21 Apr 2022 14:23:52 +0200 Subject: [PATCH] Add middleware and guards --- client/src/router/guards.ts | 49 +++++++++++++++++++++ client/src/router/index.ts | 16 +++++-- client/src/stores/counter.ts | 16 ------- client/src/stores/user.ts | 19 ++++++++ client/src/views/ProfileView.vue | 6 +-- server/config/settings/base.py | 1 + server/vbv_lernwelt/core/middleware/auth.py | 20 +++++++++ 7 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 client/src/router/guards.ts delete mode 100644 client/src/stores/counter.ts create mode 100644 client/src/stores/user.ts diff --git a/client/src/router/guards.ts b/client/src/router/guards.ts new file mode 100644 index 00000000..7037ac8e --- /dev/null +++ b/client/src/router/guards.ts @@ -0,0 +1,49 @@ +import type {NavigationGuardWithThis, RouteLocationNormalized} from 'vue-router'; +import { useUserStore } from '@/stores/user' +import type {Store} from 'pinia'; + +const cookieName = 'loginStatus' +let userStore: Store | null = null + + + +export const updateLoggedIn: NavigationGuardWithThis = (_to) => { + const loggedIn = getCookieValue(cookieName) === 'true' + const store = getUserStore() + + store.$patch({ loggedIn }) +} + +export const redirectToLoginIfRequired: NavigationGuardWithThis = (to, _from) => { + const store = getUserStore() + if(loginRequired(to) && !store.loggedIn) { + return { name: 'home' } + } +} + +const getCookieValue = (cookieName: string): string => { + // https://stackoverflow.com/questions/5639346/what-is-the-shortest-function-for-reading-a-cookie-by-name-in-javascript + const cookieValue = document.cookie.match('(^|[^;]+)\\s*' + cookieName + '\\s*=\\s*([^;]+)') + if (!cookieValue) { + return '' + } + return cookieValue.pop() || ''; +} + +// Pina is not ready when router is called the first time by app.use(), so we need to load it here +const getUserStore = (): Store => { + if (!userStore) { + userStore = useUserStore() + } + return userStore +} + +const loginRequired = (to: RouteLocationNormalized) => { + return !to.meta?.public +} + + + +// export const unauthorizedAccess: NavigationGuardWithThis = (to) => { +// r loginRequired(to) && getCookieValue('loginStatus') !== 'true'; +// } diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 90ef16a7..810e2e81 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -1,6 +1,9 @@ import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue'; import learningPathRoutes from './learningpath.routes' +import {redirectToLoginIfRequired, updateLoggedIn} from "@/router/guards"; + +const loginUrl = '/sso/login/' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), @@ -10,15 +13,19 @@ const router = createRouter({ name: 'home', component: HomeView, meta: { - layout: 'NoNavbarDemoLayout' + layout: 'NoNavbarDemoLayout', + public: true } }, { path: '/login', component: HomeView, - beforeEnter(to, from, next) { - window.location.href = "/sso/login/"; + beforeEnter(_to, _from) { + window.location.href = loginUrl }, + meta: { + public: true + } }, { path: '/profile', @@ -32,4 +39,7 @@ const router = createRouter({ ] }) +router.beforeEach(updateLoggedIn) +router.beforeEach(redirectToLoginIfRequired) + export default router diff --git a/client/src/stores/counter.ts b/client/src/stores/counter.ts deleted file mode 100644 index 4a2d2427..00000000 --- a/client/src/stores/counter.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineStore } from 'pinia' - -export const useCounterStore = defineStore({ - id: 'counter', - state: () => ({ - counter: 0 - }), - getters: { - doubleCount: (state) => state.counter * 2 - }, - actions: { - increment() { - this.counter++ - } - } -}) diff --git a/client/src/stores/user.ts b/client/src/stores/user.ts new file mode 100644 index 00000000..1555ee95 --- /dev/null +++ b/client/src/stores/user.ts @@ -0,0 +1,19 @@ +import { defineStore } from 'pinia' +// typed state https://stackoverflow.com/questions/71012513/when-using-pinia-and-typescript-how-do-you-use-an-action-to-set-the-state +export const useUserStore = defineStore({ + id: 'user', + state: () => ({ + email: '', + loggedIn: false + }), + getters: { + }, + actions: { + setEmail (email: string) { + this.email = email + }, + setLoggedIn (isLoggedIn: boolean) { + this.loggedIn = isLoggedIn + } + } +}) diff --git a/client/src/views/ProfileView.vue b/client/src/views/ProfileView.vue index a3063d96..c5f45614 100644 --- a/client/src/views/ProfileView.vue +++ b/client/src/views/ProfileView.vue @@ -12,9 +12,9 @@ export default { setup () { onMounted(async () => { - const res = await getUserData(); - users.value = res.data; - console.log(res); + // const res = await getUserData(); + // users.value = res.data; + // console.log(res); }); return { diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 21bf2b68..bb1ae595 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -153,6 +153,7 @@ MIDDLEWARE = [ "django_htmx.middleware.HtmxMiddleware", "vbv_lernwelt.core.middleware.auth.AuthenticationRequiredMiddleware", "vbv_lernwelt.core.middleware.security.SecurityRequestResponseLoggingMiddleware", + "vbv_lernwelt.core.middleware.auth.UserLoggedInCookieMiddleWare", ] # STATIC diff --git a/server/vbv_lernwelt/core/middleware/auth.py b/server/vbv_lernwelt/core/middleware/auth.py index 020ca33c..b89e9c28 100644 --- a/server/vbv_lernwelt/core/middleware/auth.py +++ b/server/vbv_lernwelt/core/middleware/auth.py @@ -45,3 +45,23 @@ class DjangoViewAuthenticationExemptDRFViewSetMixin: ) view.authentication_exempt = True return view + + +# https://stackoverflow.com/questions/4898408/how-to-set-a-login-cookie-in-django +class UserLoggedInCookieMiddleWare(MiddlewareMixin): + """ + Middleware to set user cookie + If user is authenticated and there is no cookie, set the cookie, + If the user is not authenticated and the cookie remains, delete it + """ + + cookie_name = 'loginStatus' + + def process_response(self, request, response): + # if user and no cookie, set cookie + if request.user.is_authenticated and not request.COOKIES.get(self.cookie_name): + response.set_cookie(self.cookie_name, 'true') + elif not request.user.is_authenticated and request.COOKIES.get(self.cookie_name): + # else if if no user and cookie remove user cookie, logout + response.delete_cookie(self.cookie_name) + return response