Team based role auth for routes

This commit is contained in:
2024-05-23 09:32:22 -04:00
parent 55bc1acbb3
commit c03ad48615
6 changed files with 63 additions and 26 deletions

View File

@@ -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,

View File

@@ -3,7 +3,12 @@
<q-card>
<q-toolbar>
<q-toolbar-title>Select a Boat and Time</q-toolbar-title>
<q-btn icon="close" flat round dense v-close-popup />
<q-btn
icon="close"
flat
round
dense
v-close-popup />
</q-toolbar>
<q-separator />
<CalendarHeaderComponent v-model="selectedDate" />
@@ -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">
<template #head-day="{ scope }">
<div style="text-align: center; font-weight: 800">
{{ getBoatDisplayName(scope) }}
@@ -30,7 +34,9 @@
</template>
<template #day-body="{ scope }">
<div v-for="block in getBoatBlocks(scope)" :key="block.$id">
<div
v-for="block in getBoatBlocks(scope)"
:key="block.$id">
<div
class="timeblock"
:class="selectedBlock?.$id === block.$id ? 'selected' : ''"
@@ -43,9 +49,9 @@
"
:id="block.id"
@click="selectBlock($event, scope, block)"
v-close-popup
>
{{ boats[scope.columnIndex].name }}<br />
v-close-popup>
{{ boats[scope.columnIndex].name }}
<br />
{{
selectedBlock?.$id === block.$id ? 'Selected' : 'Available'
}}
@@ -53,26 +59,25 @@
</div>
<div
v-for="reservation in getBoatReservations(scope)"
:key="reservation.$id"
>
:key="reservation.$id">
<div
class="reservation"
class="reservation column"
:style="
reservationStyles(
reservation,
scope.timeStartPos,
scope.timeDurationHeight
)
"
>
{{ getUserName(reservation.user) || 'loading...' }}<br />
">
{{ getUserName(reservation.user) || 'loading...' }}
<br />
<q-chip icon="key">{{ reservation.reason }}</q-chip>
</div>
</div>
</template>
</QCalendarDay>
</div></q-card
>
</div>
</q-card>
</div>
</template>

View File

@@ -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)

View File

@@ -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;

View File

@@ -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',

View File

@@ -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<boolean> => {
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,