More auth / role checks for navlinks

This commit is contained in:
2024-05-23 09:55:02 -04:00
parent c03ad48615
commit b0d6ec877b
4 changed files with 58 additions and 24 deletions

View File

@@ -4,37 +4,50 @@
show-if-above show-if-above
:width="200" :width="200"
:breakpoint="1024" :breakpoint="1024"
@update:model-value="$emit('drawer-toggle')" @update:model-value="$emit('drawer-toggle')">
>
<q-scroll-area class="fit"> <q-scroll-area class="fit">
<q-list padding class="menu-list"> <q-list
<template v-for="link in enabledLinks" :key="link.name"> padding
<!-- TODO: Template this to be DRY --><q-item class="menu-list">
<template
v-for="link in enabledLinks"
:key="link.name">
<!-- TODO: Template this to be DRY -->
<q-item
clickable clickable
v-ripple v-ripple
:to="link.to" :to="link.to">
>
<q-item-section avatar> <q-item-section avatar>
<q-icon :name="link.icon" /> <q-icon :name="link.icon" />
</q-item-section> </q-item-section>
<q-item-section> {{ link.name }} </q-item-section> <q-item-section>{{ link.name }}</q-item-section>
</q-item> </q-item>
<q-list v-if="link.sublinks"> <q-list v-if="link.sublinks">
<div v-for="sublink in link.sublinks" :key="sublink.name"> <div
<q-item clickable v-ripple :to="sublink.to" class="q-ml-md"> v-for="sublink in link.sublinks"
:key="sublink.name">
<q-item
clickable
v-ripple
:to="sublink.to"
class="q-ml-md"
v-if="hasRole(sublink.requiredRoles)">
<q-item-section avatar> <q-item-section avatar>
<q-icon :name="sublink.icon" /> <q-icon :name="sublink.icon" />
</q-item-section> </q-item-section>
<q-item-section> {{ sublink.name }} </q-item-section> <q-item-section>{{ sublink.name }}</q-item-section>
</q-item> </q-item>
</div></q-list </div>
> </q-list>
</template> </template>
<q-item clickable v-ripple @click="logout()"> <q-item
<q-item-section avatar><q-icon name="logout" /></q-item-section clickable
><q-item-section>Logout</q-item-section> v-ripple
@click="logout()">
<q-item-section avatar><q-icon name="logout" /></q-item-section>
<q-item-section>Logout</q-item-section>
</q-item> </q-item>
</q-list> </q-list>
</q-scroll-area> </q-scroll-area>
@@ -44,8 +57,17 @@
<script lang="ts" setup> <script lang="ts" setup>
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { enabledLinks } from 'src/router/navlinks.js'; import { enabledLinks } from 'src/router/navlinks.js';
import { useAuthStore } from 'src/stores/auth';
import { logout } from 'boot/appwrite'; import { logout } from 'boot/appwrite';
const authStore = useAuthStore();
function hasRole(roles: string[] | undefined) {
if (roles === undefined) return true;
const hasRole = authStore.hasRequiredRole(roles);
return hasRole;
}
defineProps(['drawer']); defineProps(['drawer']);
defineEmits(['drawer-toggle']); defineEmits(['drawer-toggle']);

View File

@@ -55,7 +55,7 @@ export default route(function (/* { store, ssrContext } */) {
} }
try { try {
const hasRole = await authStore.hasRequiredRole(requiredRoles); const hasRole = authStore.hasRequiredRole(requiredRoles);
if (!hasRole) { if (!hasRole) {
return next(from); return next(from);
} }

View File

@@ -47,6 +47,7 @@ export const links = [
icon: 'edit_calendar', icon: 'edit_calendar',
front_links: false, front_links: false,
enabled: true, enabled: true,
requiredRoles: ['Schedule Admins'],
}, },
], ],
}, },

View File

@@ -1,25 +1,35 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { ID, account, functions, teams } from 'boot/appwrite'; import { ID, account, functions, teams } from 'boot/appwrite';
import { ExecutionMethod, OAuthProvider, type Models } from 'appwrite'; import { ExecutionMethod, OAuthProvider, type Models } from 'appwrite';
import { ref } from 'vue'; import { computed, ref } from 'vue';
export const useAuthStore = defineStore('auth', () => { export const useAuthStore = defineStore('auth', () => {
const currentUser = ref<Models.User<Models.Preferences> | null>(null); const currentUser = ref<Models.User<Models.Preferences> | null>(null);
const currentUserTeams = ref<Models.TeamList<Models.Preferences> | null>(
null
);
const userNames = ref<Record<string, string>>({}); const userNames = ref<Record<string, string>>({});
async function init() { async function init() {
try { try {
currentUser.value = await account.get(); currentUser.value = await account.get();
currentUserTeams.value = await teams.list();
} catch { } catch {
currentUser.value = null; currentUser.value = null;
currentUserTeams.value = null;
} }
} }
const hasRequiredRole = async (requiredRoles: string[]): Promise<boolean> => { const currentUserTeamNames = computed(() =>
const userTeams = await teams.list(); currentUserTeams.value
const userTeamNames = userTeams.teams.map((team) => team.name); ? currentUserTeams.value.teams.map((team) => team.name)
console.log(requiredRoles.some((role) => userTeamNames.includes(role))); : []
return requiredRoles.some((role) => userTeamNames.includes(role)); );
const hasRequiredRole = (requiredRoles: string[]): boolean => {
return requiredRoles.some((role) =>
currentUserTeamNames.value.includes(role)
);
}; };
async function register(email: string, password: string) { async function register(email: string, password: string) {
@@ -28,8 +38,9 @@ export const useAuthStore = defineStore('auth', () => {
} }
async function login(email: string, password: string) { async function login(email: string, password: string) {
await account.createEmailPasswordSession(email, password); await account.createEmailPasswordSession(email, password);
currentUser.value = await account.get(); init();
} }
async function googleLogin() { async function googleLogin() {
account.createOAuth2Session( account.createOAuth2Session(
OAuthProvider.Google, OAuthProvider.Google,