navigation permission and ability checker

This commit is contained in:
Julian Krauser 2024-08-26 17:56:07 +02:00
parent cb80771f7a
commit 35cba95887
6 changed files with 213 additions and 92 deletions

33
src/stores/ability.ts Normal file
View file

@ -0,0 +1,33 @@
import { defineStore } from "pinia";
import type { PermissionModule, PermissionObject, PermissionSection, PermissionType } from "../types/permissionTypes";
export const useAbilityStore = defineStore("ability", {
state: () => {
return {
permissions: {} as PermissionObject,
};
},
getters: {
can:
(state) =>
(type: PermissionType | "admin", section: PermissionSection, module?: PermissionModule): boolean => {
const permissions = state.permissions;
if (type == "admin") return permissions.admin ?? false;
if (permissions.admin) return true;
if (
(!module && permissions[section] != undefined) ||
permissions[section]?.all == "*" ||
permissions[section]?.all?.includes(type)
)
return true;
if (module && (permissions[section]?.[module] == "*" || permissions[section]?.[module]?.includes(type)))
return true;
return false;
},
},
actions: {
setAbility(permissions: PermissionObject) {
this.permissions = permissions;
},
},
});

View file

@ -1,5 +1,6 @@
import { defineStore } from "pinia";
import type { PermissionObject } from "../types/permissionTypes";
import { useAbilityStore } from "./ability";
export const useAccountStore = defineStore("account", {
state: () => {
@ -8,7 +9,6 @@ export const useAccountStore = defineStore("account", {
lastname: "" as string,
mail: "" as string,
alias: "" as string,
permissions: {} as PermissionObject,
};
},
actions: {
@ -17,12 +17,11 @@ export const useAccountStore = defineStore("account", {
localStorage.removeItem("refreshToken");
window.open("/login", "_self");
},
setAccountData(firstname: string, lastname: string, mail: string, alias: string, permissions: PermissionObject) {
setAccountData(firstname: string, lastname: string, mail: string, alias: string) {
this.firstname = firstname;
this.lastname = lastname;
this.mail = mail;
this.alias = alias;
this.permissions = permissions;
},
},
});

View file

@ -1,6 +1,6 @@
import { defineStore } from "pinia";
import { shallowRef, defineAsyncComponent } from "vue";
import { useAccountStore } from "../account";
import { useAbilityStore } from "../ability";
export interface navigationModel {
club: navigationSplitModel;
@ -31,94 +31,11 @@ export interface navigationLinkModel {
export const useNavigationStore = defineStore("navigation", {
state: () => {
const accountStore = useAccountStore();
return {
activeNavigation: "club" as topLevelNavigationType,
activeLink: null as null | navigationLinkModel,
topLevel: [
{
key: "club",
title: "Verein",
levelDefault: "#members",
},
{
key: "settings",
title: "Einstellungen",
levelDefault: "#qualification",
},
{
key: "user",
title: "Benutzer",
levelDefault: "#user",
},
] as Array<topLevelNavigationModel>,
navigation: {
club: {
mainTitle: "Verein",
main: [
{
key: "#members",
title: "Mitglieder",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
{
key: "#calendar",
title: "Termine",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
{
key: "#newsletter",
title: "Newsletter",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
{
key: "#protocol",
title: "Protokolle",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
],
},
settings: {
mainTitle: "Einstellungen",
main: [
{
key: "#qualification",
title: "Qualifikationen",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
{
key: "#award",
title: "Auszeichnungen",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
{
key: "#executive_position",
title: "Vereinsämter",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
{
key: "#communication",
title: "Mitgliederdaten",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
],
},
user: {
mainTitle: "Benutzer",
main: [
{
key: "#user",
title: "Benutzer",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
{
key: "#roles",
title: "Rollen",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
],
},
} as navigationModel,
topLevel: [] as Array<topLevelNavigationModel>,
navigation: {} as navigationModel,
componentOverwrite: null as null | any,
};
},
@ -161,5 +78,150 @@ export const useNavigationStore = defineStore("navigation", {
resetNavigation() {
this.$reset();
},
updateTopLevel() {
const abilityStore = useAbilityStore();
this.topLevel = [
...(abilityStore.can("read", "club")
? [
{
key: "club",
title: "Verein",
levelDefault: "#members",
} as topLevelNavigationModel,
]
: []),
...(abilityStore.can("read", "settings")
? [
{
key: "settings",
title: "Einstellungen",
levelDefault: "#qualification",
} as topLevelNavigationModel,
]
: []),
...(abilityStore.can("read", "user")
? [
{
key: "user",
title: "Benutzer",
levelDefault: "#user",
} as topLevelNavigationModel,
]
: []),
];
if (this.topLevel.findIndex((e) => e.key == this.activeNavigation) == -1)
this.activeNavigation = this.topLevel[0]?.key ?? "club";
},
updateNavigation() {
const abilityStore = useAbilityStore();
this.navigation = {
club: {
mainTitle: "Verein",
main: [
...(abilityStore.can("read", "club", "members")
? [
{
key: "#members",
title: "Mitglieder",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
...(abilityStore.can("read", "club", "calendar")
? [
{
key: "#calendar",
title: "Termine",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
...(abilityStore.can("read", "club", "newsletter")
? [
{
key: "#newsletter",
title: "Newsletter",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
...(abilityStore.can("read", "club", "protocoll")
? [
{
key: "#protocol",
title: "Protokolle",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
],
},
settings: {
mainTitle: "Einstellungen",
main: [
...(abilityStore.can("read", "settings", "qualification")
? [
{
key: "#qualification",
title: "Qualifikationen",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
...(abilityStore.can("read", "settings", "award")
? [
{
key: "#award",
title: "Auszeichnungen",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
...(abilityStore.can("read", "settings", "executive_position")
? [
{
key: "#executive_position",
title: "Vereinsämter",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
...(abilityStore.can("read", "settings", "communication")
? [
{
key: "#communication",
title: "Mitgliederdaten",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
],
},
user: {
mainTitle: "Benutzer",
main: [
...(abilityStore.can("read", "user", "user")
? [
{
key: "#user",
title: "Benutzer",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
...(abilityStore.can("admin", "user", "role")
? [
{
key: "#role",
title: "Rollen",
component: shallowRef(defineAsyncComponent(() => import("@/views/admin/members/Overview.vue"))),
},
]
: []),
],
},
} as navigationModel;
if (this.topLevel.findIndex((e) => e.key == this.activeLink?.key) == -1) this.setLink(null);
},
},
});