From c03ad486153d6c80931917610be992fcdc60f816 Mon Sep 17 00:00:00 2001 From: Patrick Toal Date: Thu, 23 May 2024 09:32:22 -0400 Subject: [PATCH] Team based role auth for routes --- src/boot/appwrite.ts | 3 ++ .../boat/BoatScheduleTableComponent.vue | 35 ++++++++++-------- src/pages/schedule/BoatReservationPage.vue | 1 - src/router/index.ts | 36 +++++++++++++++---- src/router/routes.ts | 4 +-- src/stores/auth.ts | 10 +++++- 6 files changed, 63 insertions(+), 26 deletions(-) diff --git a/src/boot/appwrite.ts b/src/boot/appwrite.ts index 943299a..36a80e7 100644 --- a/src/boot/appwrite.ts +++ b/src/boot/appwrite.ts @@ -6,6 +6,7 @@ import { Functions, ID, AppwriteException, + Teams, } from 'appwrite'; import { useAuthStore } from 'src/stores/auth'; import { Dialog, Notify } from 'quasar'; @@ -67,6 +68,7 @@ const AppwriteIds = process.env.DEV const account = new Account(client); const databases = new Databases(client); const functions = new Functions(client); +const teams = new Teams(client); let appRouter: Router; @@ -136,6 +138,7 @@ async function login(email: string, password: string) { export { client, account, + teams, databases, functions, ID, diff --git a/src/components/scheduling/boat/BoatScheduleTableComponent.vue b/src/components/scheduling/boat/BoatScheduleTableComponent.vue index 6e97bec..5887e5e 100644 --- a/src/components/scheduling/boat/BoatScheduleTableComponent.vue +++ b/src/components/scheduling/boat/BoatScheduleTableComponent.vue @@ -3,7 +3,12 @@ Select a Boat and Time - + @@ -21,8 +26,7 @@ :short-interval-label="true" v-model="selectedDate" :column-count="boats.length" - v-touch-swipe.left.right="handleSwipe" - > + v-touch-swipe.left.right="handleSwipe"> - + + diff --git a/src/pages/schedule/BoatReservationPage.vue b/src/pages/schedule/BoatReservationPage.vue index 966585d..4f0ecee 100644 --- a/src/pages/schedule/BoatReservationPage.vue +++ b/src/pages/schedule/BoatReservationPage.vue @@ -166,7 +166,6 @@ const onReset = () => { bookingForm.value = { ...newForm }; }; const onSubmit = () => { - console.log('SUBMIT!'); const booking = bookingForm.value; if ( !(booking.boat && booking.startDate && booking.endDate && auth.currentUser) diff --git a/src/router/index.ts b/src/router/index.ts index 506df2c..1e29224 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -9,6 +9,10 @@ import { import routes from './routes'; import { useAuthStore } from 'src/stores/auth'; +const publicRoutes = routes + .filter((route) => route.meta?.publicRoute) + .map((r) => r.path); + /* * If not building with SSR mode, you can * directly export the Router instantiation; @@ -35,15 +39,33 @@ export default route(function (/* { store, ssrContext } */) { history: createHistory(process.env.VUE_ROUTER_BASE), }); - Router.beforeEach((to) => { - const publicPages = routes - .filter((route) => route.meta?.publicRoute) - .map((r) => r.path); - const authRequired = !publicPages.includes(to.path); + Router.beforeEach(async (to, from, next) => { + const authStore = useAuthStore(); + const currentUser = authStore.currentUser; + const authRequired = !publicRoutes.includes(to.path); + const requiredRoles = to.meta?.requiredRoles as string[]; - if (authRequired && !useAuthStore().currentUser) { - return '/login'; + if (authRequired && !currentUser) { + return next('/login'); } + + if (requiredRoles) { + if (!currentUser) { + return next('/login'); + } + + try { + const hasRole = await authStore.hasRequiredRole(requiredRoles); + if (!hasRole) { + return next(from); + } + } catch (error) { + console.error('Failed to fetch user teams:', error); + return next('/error'); // Redirect to an error page or handle it as needed + } + } + + next(); }); return Router; diff --git a/src/router/routes.ts b/src/router/routes.ts index 18e989d..8e4c7e2 100644 --- a/src/router/routes.ts +++ b/src/router/routes.ts @@ -44,7 +44,7 @@ const routes: RouteRecordRaw[] = [ path: 'manage', component: () => import('src/pages/schedule/ManageCalendar.vue'), name: 'manage-schedule', - meta: { requiresScheduleAdmin: true }, + meta: { requiredRoles: ['Schedule Admins'] }, }, ], }, @@ -102,7 +102,7 @@ const routes: RouteRecordRaw[] = [ { path: '/admin', component: () => import('layouts/AdminLayout.vue'), - meta: { requiresAdmin: true }, + meta: { requiredRoles: ['admin'] }, children: [ { path: '/user', diff --git a/src/stores/auth.ts b/src/stores/auth.ts index a14448d..f4d2e38 100644 --- a/src/stores/auth.ts +++ b/src/stores/auth.ts @@ -1,5 +1,5 @@ import { defineStore } from 'pinia'; -import { ID, account, functions } from 'boot/appwrite'; +import { ID, account, functions, teams } from 'boot/appwrite'; import { ExecutionMethod, OAuthProvider, type Models } from 'appwrite'; import { ref } from 'vue'; @@ -15,6 +15,13 @@ export const useAuthStore = defineStore('auth', () => { } } + const hasRequiredRole = async (requiredRoles: string[]): Promise => { + const userTeams = await teams.list(); + const userTeamNames = userTeams.teams.map((team) => team.name); + console.log(requiredRoles.some((role) => userTeamNames.includes(role))); + return requiredRoles.some((role) => userTeamNames.includes(role)); + }; + async function register(email: string, password: string) { await account.create(ID.unique(), email, password); return await login(email, password); @@ -65,6 +72,7 @@ export const useAuthStore = defineStore('auth', () => { return { currentUser, getUserNameById, + hasRequiredRole, register, login, googleLogin,