Skip to content

Commit

Permalink
Permission checks for pages (#166)
Browse files Browse the repository at this point in the history
* generic permission middleware

* placeholder middleware implementations

* implement isAdmin check

* permissions on admin page + fix typo

* logical functions for conditions

* pass MiddlewareContext to conditions

* permission to check if user is part of subject

* permission to check if user can create a project for a subject

* run formatter

* restructure navigation guard middlewares

* subject details permissions

* project details permissions

* go to /not-found instead of /forbidden

* create project permissions

* fix project permission checks

* permissions for submit page

* permissions for groups overview

* permissions for group details

* permissions for submission list of project

* remove unused imports

* Apply suggestions from code review

Co-authored-by: Xander Bil <[email protected]>

* permissions for new pages

* allow admins to visit all pages

---------

Co-authored-by: Xander Bil <[email protected]>
  • Loading branch information
reyniersbram and xerbalind authored May 22, 2024
1 parent 34d3314 commit 889e485
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 82 deletions.
7 changes: 4 additions & 3 deletions frontend/src/composables/useIsAdmin.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { computed } from "vue";
import { useCurrentUserQuery } from "@/queries/User";
import type { QueryClient } from "@tanstack/vue-query";

export default function useIsAdmin() {
const { data: user } = useCurrentUserQuery();
export default function useIsAdmin(queryClient?: QueryClient) {
const { data: user, isLoading } = useCurrentUserQuery(queryClient);
const isAdmin = computed(() => user.value?.is_admin || false);
return { isAdmin };
return { isAdmin, isLoading };
}
7 changes: 4 additions & 3 deletions frontend/src/composables/useIsTeacher.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { computed } from "vue";
import { useCurrentUserQuery } from "@/queries/User";
import type { QueryClient } from "@tanstack/vue-query";

export default function useIsTeacher() {
const { data: user } = useCurrentUserQuery();
export default function useIsTeacher(queryClient?: QueryClient) {
const { data: user, isLoading } = useCurrentUserQuery(queryClient);
const isTeacher = computed(() => user.value?.is_teacher || false);
return { isTeacher };
return { isTeacher, isLoading };
}
7 changes: 5 additions & 2 deletions frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import "@mdi/font/css/materialdesignicons.css";

import { createApp } from "vue";
import { createPinia } from "pinia";
import { VueQueryPlugin } from "@tanstack/vue-query";
import { VueQueryPlugin, QueryClient } from "@tanstack/vue-query";

import App from "./App.vue";
import router from "./router";
Expand All @@ -16,10 +16,13 @@ const app = createApp(App);

const pinia = createPinia();

const queryClient = new QueryClient();
app.provide("queryClient", queryClient);

app.use(router);
app.use(pinia);
app.use(vuetify);
app.use(i18n);
app.use(VueQueryPlugin);
app.use(VueQueryPlugin, { queryClient });

app.mount("#app");
58 changes: 36 additions & 22 deletions frontend/src/queries/Group.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { computed, toValue } from "vue";
import type { MaybeRefOrGetter } from "vue";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import type { UseMutationReturnType, UseQueryReturnType } from "@tanstack/vue-query";
import type { QueryClient, UseMutationReturnType, UseQueryReturnType } from "@tanstack/vue-query";
import type Group from "@/models/Group";
import type { GroupForm } from "@/models/Group";
import {
Expand Down Expand Up @@ -38,26 +38,34 @@ function PROJECT_USER_GROUP_QUERY_KEY(projectId: number): (string | number)[] {
* Query composable for fetching a group by id
*/
export function useGroupQuery(
groupId: MaybeRefOrGetter<number | undefined>
groupId: MaybeRefOrGetter<number | undefined>,
queryClient?: QueryClient
): UseQueryReturnType<Group, Error> {
return useQuery<Group, Error>({
queryKey: GROUP_QUERY_KEY(toValue(groupId)!),
queryFn: () => getGroup(toValue(groupId)!),
enabled: () => !!toValue(groupId),
});
return useQuery<Group, Error>(
{
queryKey: GROUP_QUERY_KEY(toValue(groupId)!),
queryFn: () => getGroup(toValue(groupId)!),
enabled: () => !!toValue(groupId),
},
queryClient
);
}

/**
* Query composable for fetching all groups of a project
*/
export function useProjectGroupsQuery(
projectId: MaybeRefOrGetter<number | undefined>
projectId: MaybeRefOrGetter<number | undefined>,
queryClient?: QueryClient
): UseQueryReturnType<Group[], Error> {
return useQuery<Group[], Error>({
queryKey: computed(() => PROJECT_GROUPS_QUERY_KEY(toValue(projectId)!)),
queryFn: () => getProjectGroups(toValue(projectId)!),
enabled: !!toValue(projectId),
});
return useQuery<Group[], Error>(
{
queryKey: computed(() => PROJECT_GROUPS_QUERY_KEY(toValue(projectId)!)),
queryFn: () => getProjectGroups(toValue(projectId)!),
enabled: !!toValue(projectId),
},
queryClient
);
}

export function useUserGroupsQuery(): UseQueryReturnType<Group[], Error> {
Expand All @@ -73,20 +81,26 @@ export function useUserGroupsQuery(): UseQueryReturnType<Group[], Error> {
* @returns The group the user is in for the project, undefined if the user is not in a group
*/
export function useProjectGroupQuery(
projectId: MaybeRefOrGetter<number | undefined>
projectId: MaybeRefOrGetter<number | undefined>,
queryClient?: QueryClient
): UseQueryReturnType<Group | null, Error> {
const { data: projectGroups } = useProjectGroupsQuery(projectId);
const { data: user } = useCurrentUserQuery();
const userGroup = computed(
() =>
const { data: projectGroups } = useProjectGroupsQuery(projectId, queryClient);
const { data: user } = useCurrentUserQuery(queryClient);
const userGroup = computed(() => {
if (projectGroups.value === undefined || user.value === undefined) return undefined;
return (
projectGroups.value?.find((group) =>
group.members.some((member) => member.uid === user.value?.uid)
) || null
);
return useQuery({
queryKey: computed(() => PROJECT_USER_GROUP_QUERY_KEY(toValue(projectId)!)),
queryFn: () => userGroup,
);
});
return useQuery(
{
queryKey: computed(() => PROJECT_USER_GROUP_QUERY_KEY(toValue(projectId)!)),
queryFn: () => userGroup,
},
queryClient
);
}

/**
Expand Down
33 changes: 21 additions & 12 deletions frontend/src/queries/Project.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { computed, toValue } from "vue";
import type { MaybeRefOrGetter } from "vue";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import type { UseMutationReturnType, UseQueryReturnType } from "@tanstack/vue-query";
import type { QueryClient, UseMutationReturnType, UseQueryReturnType } from "@tanstack/vue-query";
import type { ProjectForm, UserProjectList } from "@/models/Project";
import type Project from "@/models/Project";
import {
Expand Down Expand Up @@ -31,23 +31,32 @@ function TEST_FILES_QUERY_KEY(projectId: number): (string | number)[] {

// Hook for fetching project details
export function useProjectQuery(
projectId: MaybeRefOrGetter<number | undefined>
projectId: MaybeRefOrGetter<number | undefined>,
queryClient?: QueryClient
): UseQueryReturnType<Project, Error> {
return useQuery<Project, Error>({
queryKey: computed(() => PROJECT_QUERY_KEY(toValue(projectId)!)),
queryFn: () => getProject(toValue(projectId)!),
enabled: () => !!toValue(projectId),
});
return useQuery<Project, Error>(
{
queryKey: computed(() => PROJECT_QUERY_KEY(toValue(projectId)!)),
queryFn: () => getProject(toValue(projectId)!),
enabled: () => !!toValue(projectId),
},
queryClient
);
}

/**
* Query composable for fetching all projects of the current user
*/
export function useProjectsQuery(): UseQueryReturnType<UserProjectList, Error> {
return useQuery<UserProjectList>({
queryKey: PROJECTS_QUERY_KEY(),
queryFn: getProjects,
});
export function useProjectsQuery(
queryClient?: QueryClient
): UseQueryReturnType<UserProjectList, Error> {
return useQuery<UserProjectList>(
{
queryKey: PROJECTS_QUERY_KEY(),
queryFn: getProjects,
},
queryClient
);
}

/**
Expand Down
17 changes: 11 additions & 6 deletions frontend/src/queries/Subject.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { computed, toValue } from "vue";
import type { MaybeRefOrGetter } from "vue";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import type { UseQueryReturnType, UseMutationReturnType } from "@tanstack/vue-query";
import type { UseQueryReturnType, UseMutationReturnType, QueryClient } from "@tanstack/vue-query";
import {
getSubject,
getSubjectInstructors,
Expand Down Expand Up @@ -74,11 +74,16 @@ export function useSubjectUuidQuery(
/**
* Query composable for fetching all subjects of the current user
*/
export function useSubjectsQuery(): UseQueryReturnType<UserSubjectList, Error> {
return useQuery<UserSubjectList, Error>({
queryKey: SUBJECTS_QUERY_KEY(),
queryFn: getSubjects,
});
export function useSubjectsQuery(
queryClient?: QueryClient
): UseQueryReturnType<UserSubjectList, Error> {
return useQuery<UserSubjectList, Error>(
{
queryKey: SUBJECTS_QUERY_KEY(),
queryFn: getSubjects,
},
queryClient
);
}

/**
Expand Down
15 changes: 9 additions & 6 deletions frontend/src/queries/User.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { computed, toValue } from "vue";
import type { MaybeRefOrGetter } from "vue";
import { useQuery, useMutation, useQueryClient } from "@tanstack/vue-query";
import type { UseQueryReturnType, UseMutationReturnType } from "@tanstack/vue-query";
import type { UseQueryReturnType, UseMutationReturnType, QueryClient } from "@tanstack/vue-query";
import type User from "@/models/User";
import {
getCurrentUser,
Expand All @@ -27,11 +27,14 @@ export function USERS_QUERY_KEY(): string[] {
/**
* Query composable for fetching the current user
*/
export function useCurrentUserQuery(): UseQueryReturnType<User, Error> {
return useQuery<User, Error>({
queryKey: CURRENT_USER_QUERY_KEY(),
queryFn: () => getCurrentUser(),
});
export function useCurrentUserQuery(queryClient?: QueryClient): UseQueryReturnType<User, Error> {
return useQuery<User, Error>(
{
queryKey: CURRENT_USER_QUERY_KEY(),
queryFn: () => getCurrentUser(),
},
queryClient
);
}

/**
Expand Down
Loading

0 comments on commit 889e485

Please sign in to comment.