More auth / role checks for navlinks
This commit is contained in:
@@ -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']);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user