From fc0d3b5c84f1dd898a25788c92be84680a9683a5 Mon Sep 17 00:00:00 2001 From: Gabriellvl Date: Thu, 15 May 2025 20:54:43 +0200 Subject: [PATCH] refactor: enum voor account types --- backend/src/controllers/auth.ts | 3 +- .../auth/checks/assignment-auth-checks.ts | 3 +- .../src/middleware/auth/checks/auth-checks.ts | 5 +- .../auth/checks/class-auth-checks.ts | 9 +- .../auth/checks/group-auth-checker.ts | 3 +- .../checks/learning-content-auth-checks.ts | 3 +- .../middleware/auth/checks/question-checks.ts | 3 +- .../auth/checks/submission-checks.ts | 3 +- common/src/util/account-types.ts | 4 + frontend/package.json | 1 + frontend/src/components/SingleQuestion.vue | 3 +- frontend/src/views/LoginPage.vue | 5 +- .../views/assignments/CreateAssignment.vue | 3 +- .../views/assignments/SingleAssignment.vue | 3 +- .../src/views/assignments/UserAssignments.vue | 3 +- frontend/src/views/classes/UserClasses.vue | 3 +- .../views/learning-paths/LearningPathPage.vue | 15 +- .../learning-object/LearningObjectView.vue | 3 +- .../submissions/SubmitButton.vue | 3 +- package-lock.json | 5869 +---------------- 20 files changed, 77 insertions(+), 5870 deletions(-) create mode 100644 common/src/util/account-types.ts diff --git a/backend/src/controllers/auth.ts b/backend/src/controllers/auth.ts index ca79da59..2daace17 100644 --- a/backend/src/controllers/auth.ts +++ b/backend/src/controllers/auth.ts @@ -5,6 +5,7 @@ import { envVars, getEnvVar } from '../util/envVars.js'; import { createOrUpdateStudent } from '../services/students.js'; import { Request, Response } from 'express'; import { createOrUpdateTeacher } from '../services/teachers.js'; +import {AccountType} from "@dwengo-1/common/util/account-types"; interface FrontendIdpConfig { authority: string; @@ -55,7 +56,7 @@ export async function postHelloHandler(req: AuthenticatedRequest, res: Response) firstName: auth.firstName ?? '', lastName: auth.lastName ?? '', }; - if (auth.accountType === 'student') { + if (auth.accountType === AccountType.Student) { await createOrUpdateStudent(userData); logger.debug(`Synchronized student ${userData.username} with IDP`); } else { diff --git a/backend/src/middleware/auth/checks/assignment-auth-checks.ts b/backend/src/middleware/auth/checks/assignment-auth-checks.ts index f8e1e3d7..c812ef43 100644 --- a/backend/src/middleware/auth/checks/assignment-auth-checks.ts +++ b/backend/src/middleware/auth/checks/assignment-auth-checks.ts @@ -2,6 +2,7 @@ import { authorize } from './auth-checks.js'; import { fetchClass } from '../../../services/classes.js'; import { fetchAllGroups } from '../../../services/groups.js'; import { mapToUsername } from '../../../interfaces/user.js'; +import {AccountType} from "@dwengo-1/common/util/account-types"; /** * Expects the path to contain the path parameters 'classId' and 'id' (meaning the ID of the assignment). @@ -11,7 +12,7 @@ import { mapToUsername } from '../../../interfaces/user.js'; */ export const onlyAllowIfHasAccessToAssignment = authorize(async (auth, req) => { const { classid: classId, id: assignmentId } = req.params as { classid: string; id: number }; - if (auth.accountType === 'teacher') { + if (auth.accountType === AccountType.Teacher) { const clazz = await fetchClass(classId); return clazz.teachers.map(mapToUsername).includes(auth.username); } diff --git a/backend/src/middleware/auth/checks/auth-checks.ts b/backend/src/middleware/auth/checks/auth-checks.ts index 6afe92e7..9fe7903d 100644 --- a/backend/src/middleware/auth/checks/auth-checks.ts +++ b/backend/src/middleware/auth/checks/auth-checks.ts @@ -4,6 +4,7 @@ import * as express from 'express'; import { RequestHandler } from 'express'; import { UnauthorizedException } from '../../../exceptions/unauthorized-exception.js'; import { ForbiddenException } from '../../../exceptions/forbidden-exception.js'; +import {AccountType} from "@dwengo-1/common/util/account-types"; /** * Middleware which rejects unauthenticated users (with HTTP 401) and authenticated users which do not fulfill @@ -36,11 +37,11 @@ export const authenticatedOnly = authorize((_) => true); /** * Middleware which rejects requests from unauthenticated users or users that aren't students. */ -export const studentsOnly = authorize((auth) => auth.accountType === 'student'); +export const studentsOnly = authorize((auth) => auth.accountType === AccountType.Student); /** * Middleware which rejects requests from unauthenticated users or users that aren't teachers. */ -export const teachersOnly = authorize((auth) => auth.accountType === 'teacher'); +export const teachersOnly = authorize((auth) => auth.accountType === AccountType.Teacher); /** * Middleware which is to be used on requests no normal user should be able to execute. * Since there is no concept of administrator accounts yet, currently, those requests will always be blocked. diff --git a/backend/src/middleware/auth/checks/class-auth-checks.ts b/backend/src/middleware/auth/checks/class-auth-checks.ts index 6af44827..21272fec 100644 --- a/backend/src/middleware/auth/checks/class-auth-checks.ts +++ b/backend/src/middleware/auth/checks/class-auth-checks.ts @@ -4,6 +4,7 @@ import { AuthenticatedRequest } from '../authenticated-request.js'; import { fetchClass } from '../../../services/classes.js'; import { mapToUsername } from '../../../interfaces/user.js'; import {getAllInvitations} from "../../../services/teacher-invitations"; +import {AccountType} from "@dwengo-1/common/util/account-types"; async function teaches(teacherUsername: string, classId: string): Promise { const clazz = await fetchClass(classId); @@ -18,7 +19,7 @@ async function teaches(teacherUsername: string, classId: string): Promise { if (req.params.username === auth.username) { return true; - } else if (auth.accountType === 'teacher') { + } else if (auth.accountType === AccountType.Teacher) { return teaches(auth.username, req.params.classId); } return false; @@ -39,7 +40,7 @@ export const onlyAllowTeacherOfClass = authorize( export const onlyAllowIfInClass = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { const classId = req.params.classId ?? req.params.classid ?? req.params.id; const clazz = await fetchClass(classId); - if (auth.accountType === 'teacher') { + if (auth.accountType === AccountType.Teacher) { return clazz.teachers.map(mapToUsername).includes(auth.username); } return clazz.students.map(mapToUsername).includes(auth.username); @@ -48,7 +49,7 @@ export const onlyAllowIfInClass = authorize(async (auth: AuthenticationInfo, req export const onlyAllowIfInClassOrInvited = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { const classId = req.params.classId ?? req.params.classid ?? req.params.id; const clazz = await fetchClass(classId); - if (auth.accountType === 'teacher') { + if (auth.accountType === AccountType.Teacher) { const invitations = await getAllInvitations(auth.username, false); return clazz.teachers.map(mapToUsername).includes(auth.username) || invitations.some(invitation => invitation.classId === classId); } @@ -62,7 +63,7 @@ export const onlyAllowOwnClassInBody = authorize(async (auth, req) => { const classId = (req.body as { class: string })?.class; const clazz = await fetchClass(classId); - if (auth.accountType === 'teacher') { + if (auth.accountType === AccountType.Teacher) { return clazz.teachers.map(mapToUsername).includes(auth.username); } return clazz.students.map(mapToUsername).includes(auth.username); diff --git a/backend/src/middleware/auth/checks/group-auth-checker.ts b/backend/src/middleware/auth/checks/group-auth-checker.ts index 02230b13..408dee7d 100644 --- a/backend/src/middleware/auth/checks/group-auth-checker.ts +++ b/backend/src/middleware/auth/checks/group-auth-checker.ts @@ -2,6 +2,7 @@ import { authorize } from './auth-checks.js'; import { fetchClass } from '../../../services/classes.js'; import { fetchGroup } from '../../../services/groups.js'; import { mapToUsername } from '../../../interfaces/user.js'; +import {AccountType} from "@dwengo-1/common/util/account-types"; /** * Expects the path to contain the path parameters 'classid', 'assignmentid' and 'groupid'. @@ -16,7 +17,7 @@ export const onlyAllowIfHasAccessToGroup = authorize(async (auth, req) => { groupid: groupId, } = req.params as { classid: string; assignmentid: number; groupid: number }; - if (auth.accountType === 'teacher') { + if (auth.accountType === AccountType.Teacher) { const clazz = await fetchClass(classId); return clazz.teachers.map(mapToUsername).includes(auth.username); } // User is student diff --git a/backend/src/middleware/auth/checks/learning-content-auth-checks.ts b/backend/src/middleware/auth/checks/learning-content-auth-checks.ts index c0b34c52..fde71181 100644 --- a/backend/src/middleware/auth/checks/learning-content-auth-checks.ts +++ b/backend/src/middleware/auth/checks/learning-content-auth-checks.ts @@ -1,6 +1,7 @@ import { authorize } from './auth-checks'; import { AuthenticationInfo } from '../authentication-info'; import { AuthenticatedRequest } from '../authenticated-request'; +import {AccountType} from "@dwengo-1/common/util/account-types"; /** * Only allows requests whose learning path personalization query parameters ('forGroup' / 'assignmentNo' / 'classId') @@ -11,7 +12,7 @@ import { AuthenticatedRequest } from '../authenticated-request'; */ export const onlyAllowPersonalizationForOwnGroup = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { const { forGroup, assignmentNo, classId } = req.params; - if (auth.accountType === 'student' && forGroup && assignmentNo && classId) { + if (auth.accountType === AccountType.Student && forGroup && assignmentNo && classId) { // TODO: groupNumber? // Const group = await fetchGroup(Number(classId), Number(assignmentNo), ) return false; diff --git a/backend/src/middleware/auth/checks/question-checks.ts b/backend/src/middleware/auth/checks/question-checks.ts index 374a2aa0..fa306f8c 100644 --- a/backend/src/middleware/auth/checks/question-checks.ts +++ b/backend/src/middleware/auth/checks/question-checks.ts @@ -7,6 +7,7 @@ import { fetchQuestion } from '../../../services/questions.js'; import { FALLBACK_SEQ_NUM } from '../../../config.js'; import { fetchAnswer } from '../../../services/answers.js'; import { mapToUsername } from '../../../interfaces/user.js'; +import {AccountType} from "@dwengo-1/common/util/account-types"; export const onlyAllowAuthor = authorize( (auth: AuthenticationInfo, req: AuthenticatedRequest) => (req.body as { author: string }).author === auth.username @@ -57,7 +58,7 @@ export const onlyAllowIfHasAccessToQuestion = authorize(async (auth: Authenticat const question = await fetchQuestion(questionId); const group = question.inGroup; - if (auth.accountType === 'teacher') { + if (auth.accountType === AccountType.Teacher) { const cls = group.assignment.within; // TODO check if contains full objects return cls.teachers.map(mapToUsername).includes(auth.username); } // User is student diff --git a/backend/src/middleware/auth/checks/submission-checks.ts b/backend/src/middleware/auth/checks/submission-checks.ts index cb84f438..df7282f6 100644 --- a/backend/src/middleware/auth/checks/submission-checks.ts +++ b/backend/src/middleware/auth/checks/submission-checks.ts @@ -6,6 +6,7 @@ import { AuthenticationInfo } from '../authentication-info.js'; import { authorize } from './auth-checks.js'; import { FALLBACK_LANG } from '../../../config.js'; import { mapToUsername } from '../../../interfaces/user.js'; +import {AccountType} from "@dwengo-1/common/util/account-types"; export const onlyAllowSubmitter = authorize( (auth: AuthenticationInfo, req: AuthenticatedRequest) => (req.body as { submitter: string }).submitter === auth.username @@ -18,7 +19,7 @@ export const onlyAllowIfHasAccessToSubmission = authorize(async (auth: Authentic const loId = new LearningObjectIdentifier(lohruid, languageMap[lang as string] ?? FALLBACK_LANG, Number(version)); const submission = await fetchSubmission(loId, Number(submissionNumber)); - if (auth.accountType === 'teacher') { + if (auth.accountType === AccountType.Teacher) { // Dit kan niet werken om dat al deze objecten niet gepopulate zijn. return submission.onBehalfOf.assignment.within.teachers.map(mapToUsername).includes(auth.username); } diff --git a/common/src/util/account-types.ts b/common/src/util/account-types.ts new file mode 100644 index 00000000..61c761dc --- /dev/null +++ b/common/src/util/account-types.ts @@ -0,0 +1,4 @@ +export enum AccountType { + Student = 'student', + Teacher = 'teacher' +} diff --git a/frontend/package.json b/frontend/package.json index 0826edae..86232620 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "test:e2e": "playwright test" }, "dependencies": { + "@dwengo-1/common": "^0.2.0", "@tanstack/react-query": "^5.69.0", "@tanstack/vue-query": "^5.69.0", "@vueuse/core": "^13.1.0", diff --git a/frontend/src/components/SingleQuestion.vue b/frontend/src/components/SingleQuestion.vue index 8d53b6e1..51ee3469 100644 --- a/frontend/src/components/SingleQuestion.vue +++ b/frontend/src/components/SingleQuestion.vue @@ -6,6 +6,7 @@ import type { AnswersResponse } from "@/controllers/answers"; import type { AnswerData, AnswerDTO } from "@dwengo-1/common/interfaces/answer"; import authService from "@/services/auth/auth-service"; + import {AccountType} from "@dwengo-1/common/util/account-types"; const props = defineProps<{ question: QuestionDTO; @@ -80,7 +81,7 @@ {{ question.content }}
{ - await auth.loginAs("student"); + await auth.loginAs(AccountType.Student); } async function loginAsTeacher(): Promise { - await auth.loginAs("teacher"); + await auth.loginAs(AccountType.Teacher); } diff --git a/frontend/src/views/assignments/CreateAssignment.vue b/frontend/src/views/assignments/CreateAssignment.vue index 32cda330..fdbde1ce 100644 --- a/frontend/src/views/assignments/CreateAssignment.vue +++ b/frontend/src/views/assignments/CreateAssignment.vue @@ -14,6 +14,7 @@ import type { AssignmentDTO } from "@dwengo-1/common/interfaces/assignment"; import { useCreateAssignmentMutation } from "@/queries/assignments.ts"; import { useRoute } from "vue-router"; + import {AccountType} from "@dwengo-1/common/util/account-types"; const route = useRoute(); const router = useRouter(); @@ -23,7 +24,7 @@ onMounted(async () => { // Redirect student - if (role.value === "student") { + if (role.value === AccountType.Student) { await router.push("/user"); } diff --git a/frontend/src/views/assignments/SingleAssignment.vue b/frontend/src/views/assignments/SingleAssignment.vue index 3d9f7f0a..bf806a59 100644 --- a/frontend/src/views/assignments/SingleAssignment.vue +++ b/frontend/src/views/assignments/SingleAssignment.vue @@ -8,9 +8,10 @@ import { useGetLearningPathQuery } from "@/queries/learning-paths.ts"; import type { LearningPath } from "@/data-objects/learning-paths/learning-path.ts"; import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; + import {AccountType} from "@dwengo-1/common/util/account-types"; const role = auth.authState.activeRole; - const isTeacher = computed(() => role === "teacher"); + const isTeacher = computed(() => role === AccountType.Teacher); const route = useRoute(); const classId = ref(route.params.classId as string); diff --git a/frontend/src/views/assignments/UserAssignments.vue b/frontend/src/views/assignments/UserAssignments.vue index afb7a380..f1594a7c 100644 --- a/frontend/src/views/assignments/UserAssignments.vue +++ b/frontend/src/views/assignments/UserAssignments.vue @@ -9,6 +9,7 @@ import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; import { asyncComputed } from "@vueuse/core"; import { useDeleteAssignmentMutation } from "@/queries/assignments.ts"; + import {AccountType} from "@dwengo-1/common/util/account-types"; const { t } = useI18n(); const router = useRouter(); @@ -16,7 +17,7 @@ const role = ref(auth.authState.activeRole); const username = ref(""); - const isTeacher = computed(() => role.value === "teacher"); + const isTeacher = computed(() => role.value === AccountType.Teacher); // Fetch and store all the teacher's classes let classesQueryResults = undefined; diff --git a/frontend/src/views/classes/UserClasses.vue b/frontend/src/views/classes/UserClasses.vue index 566bd02a..fe730300 100644 --- a/frontend/src/views/classes/UserClasses.vue +++ b/frontend/src/views/classes/UserClasses.vue @@ -2,6 +2,7 @@ import authState from "@/services/auth/auth-service.ts"; import TeacherClasses from "./TeacherClasses.vue"; import StudentClasses from "./StudentClasses.vue"; + import {AccountType} from "@dwengo-1/common/util/account-types"; // Determine if role is student or teacher to render correct view const role: string = authState.authState.activeRole!; @@ -9,7 +10,7 @@ diff --git a/frontend/src/views/learning-paths/LearningPathPage.vue b/frontend/src/views/learning-paths/LearningPathPage.vue index dc444156..865668fe 100644 --- a/frontend/src/views/learning-paths/LearningPathPage.vue +++ b/frontend/src/views/learning-paths/LearningPathPage.vue @@ -22,6 +22,7 @@ import type { AssignmentDTO } from "@dwengo-1/common/interfaces/assignment"; import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; import QuestionNotification from "@/components/QuestionNotification.vue"; + import {AccountType} from "@dwengo-1/common/util/account-types"; const router = useRouter(); const route = useRoute(); @@ -235,8 +236,8 @@

- - +