diff --git a/backend/src/middleware/auth/checks/question-checks.ts b/backend/src/middleware/auth/checks/question-checks.ts index 63b47e7b..bfe76061 100644 --- a/backend/src/middleware/auth/checks/question-checks.ts +++ b/backend/src/middleware/auth/checks/question-checks.ts @@ -6,6 +6,7 @@ import {getLearningObjectId, getQuestionId} from "../../../controllers/questions import {fetchQuestion} from "../../../services/questions"; import {FALLBACK_SEQ_NUM} from "../../../config"; import {fetchAnswer} from "../../../services/answers"; +import {mapToUsername} from "../../../interfaces/user"; export const onlyAllowAuthor = authorize( (auth: AuthenticationInfo, req: AuthenticatedRequest) => req.body.author === auth.username @@ -46,3 +47,26 @@ export const onlyAllowAuthorRequestAnswer = authorize( return answer.author.username == auth.username; } ); + +export const onlyAllowIfHasAccessToQuestion = authorize( + async (auth, req) => { + const hruid = req.params.hruid; + const version = req.params.version; + const language = req.query.lang as string; + const seq = req.params.seq; + requireFields({ hruid }); + + const learningObjectId = getLearningObjectId(hruid, version, language); + const questionId = getQuestionId(learningObjectId, seq); + + const question = await fetchQuestion(questionId); + const group = question.inGroup; + + if (auth.accountType === "teacher") { + const cls = group.assignment.within; // TODO check if contains full objects + return cls.teachers.map(mapToUsername).includes(auth.username); + } else { // user is student + return group.members.map(mapToUsername).includes(auth.username); + } + } +); diff --git a/backend/src/routes/answers.ts b/backend/src/routes/answers.ts index 0f11c173..2571c56d 100644 --- a/backend/src/routes/answers.ts +++ b/backend/src/routes/answers.ts @@ -1,7 +1,11 @@ import express from 'express'; import { createAnswerHandler, deleteAnswerHandler, getAnswerHandler, getAllAnswersHandler, updateAnswerHandler } from '../controllers/answers.js'; import {adminOnly, authenticatedOnly, teachersOnly} from "../middleware/auth/checks/auth-checks"; -import {onlyAllowAuthor, onlyAllowAuthorRequestAnswer} from "../middleware/auth/checks/question-checks"; +import { + onlyAllowAuthor, + onlyAllowAuthorRequestAnswer, + onlyAllowIfHasAccessToQuestion +} from "../middleware/auth/checks/question-checks"; const router = express.Router({ mergeParams: true }); @@ -10,7 +14,7 @@ router.get('/', adminOnly, getAllAnswersHandler); router.post('/', teachersOnly, onlyAllowAuthor, createAnswerHandler); -router.get('/:seqAnswer', authenticatedOnly, getAnswerHandler); +router.get('/:seqAnswer', onlyAllowIfHasAccessToQuestion, getAnswerHandler); router.delete('/:seqAnswer', teachersOnly, onlyAllowAuthorRequestAnswer, deleteAnswerHandler); diff --git a/backend/src/routes/questions.ts b/backend/src/routes/questions.ts index 287a242b..1363ae1a 100644 --- a/backend/src/routes/questions.ts +++ b/backend/src/routes/questions.ts @@ -3,7 +3,11 @@ import { createQuestionHandler, deleteQuestionHandler, getAllQuestionsHandler, g import answerRoutes from './answers.js'; import {adminOnly, authenticatedOnly, studentsOnly} from "../middleware/auth/checks/auth-checks"; import {updateAnswerHandler} from "../controllers/answers"; -import {onlyAllowAuthor, onlyAllowAuthorRequest} from "../middleware/auth/checks/question-checks"; +import { + onlyAllowAuthor, + onlyAllowAuthorRequest, + onlyAllowIfHasAccessToQuestion +} from "../middleware/auth/checks/question-checks"; const router = express.Router({ mergeParams: true }); @@ -15,7 +19,7 @@ router.get('/', adminOnly, getAllQuestionsHandler); router.post('/', studentsOnly, onlyAllowAuthor, createQuestionHandler); // Information about a question with id -router.get('/:seq', authenticatedOnly, getQuestionHandler); // TODO every body in group + teachers? +router.get('/:seq', onlyAllowIfHasAccessToQuestion, getQuestionHandler); router.delete('/:seq', studentsOnly, onlyAllowAuthorRequest, deleteQuestionHandler); diff --git a/backend/src/services/questions.ts b/backend/src/services/questions.ts index 49bf9e92..38f12689 100644 --- a/backend/src/services/questions.ts +++ b/backend/src/services/questions.ts @@ -12,6 +12,7 @@ import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment'; import { fetchStudent } from './students.js'; import { NotFoundException } from '../exceptions/not-found-exception'; import { FALLBACK_VERSION_NUM } from '../config.js'; +import {ConflictException} from "../exceptions/conflict-exception"; export async function getQuestionsAboutLearningObjectInAssignment( loId: LearningObjectIdentifier, @@ -88,12 +89,20 @@ export async function createQuestion(loId: LearningObjectIdentifier, questionDat const clazz = await getClassRepository().findById((questionData.inGroup.assignment as AssignmentDTO).within); const assignment = mapToAssignment(questionData.inGroup.assignment as AssignmentDTO, clazz!); - const inGroup = await getGroupRepository().findByAssignmentAndGroupNumber(assignment, questionData.inGroup.groupNumber); + const group = await getGroupRepository().findByAssignmentAndGroupNumber(assignment, questionData.inGroup.groupNumber); + + if (!group){ + throw new NotFoundException("Group with id and assignment not found"); + } + + if (! group.members.contains(author)) { + throw new ConflictException("Author is not part of this group"); + } const question = await questionRepository.createQuestion({ loId, author, - inGroup: inGroup!, + inGroup: group!, content, });