feat: question + answer checks
This commit is contained in:
parent
f671341bad
commit
78b65f148e
4 changed files with 67 additions and 11 deletions
48
backend/src/middleware/auth/checks/question-checks.ts
Normal file
48
backend/src/middleware/auth/checks/question-checks.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import {authorize} from "./auth-checks";
|
||||||
|
import {AuthenticationInfo} from "../authentication-info";
|
||||||
|
import {AuthenticatedRequest} from "../authenticated-request";
|
||||||
|
import {requireFields} from "../../../controllers/error-helper";
|
||||||
|
import {getLearningObjectId, getQuestionId} from "../../../controllers/questions";
|
||||||
|
import {fetchQuestion} from "../../../services/questions";
|
||||||
|
import {FALLBACK_SEQ_NUM} from "../../../config";
|
||||||
|
import {fetchAnswer} from "../../../services/answers";
|
||||||
|
|
||||||
|
export const onlyAllowAuthor = authorize(
|
||||||
|
(auth: AuthenticationInfo, req: AuthenticatedRequest) => req.body.author === auth.username
|
||||||
|
);
|
||||||
|
|
||||||
|
export const onlyAllowAuthorRequest = authorize(
|
||||||
|
(auth: AuthenticationInfo, req: AuthenticatedRequest) => {
|
||||||
|
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);
|
||||||
|
|
||||||
|
return question.author.username == auth.username;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const onlyAllowAuthorRequestAnswer = authorize(
|
||||||
|
(auth: AuthenticationInfo, req: AuthenticatedRequest) => {
|
||||||
|
const hruid = req.params.hruid;
|
||||||
|
const version = req.params.version;
|
||||||
|
const language = req.query.lang as string;
|
||||||
|
const seq = req.params.seq;
|
||||||
|
const seqAnswer = req.params.seqAnswer;
|
||||||
|
requireFields({ hruid });
|
||||||
|
|
||||||
|
const learningObjectId = getLearningObjectId(hruid, version, language);
|
||||||
|
const questionId = getQuestionId(learningObjectId, seq);
|
||||||
|
|
||||||
|
const sequenceNumber = Number(seqAnswer) || FALLBACK_SEQ_NUM;
|
||||||
|
const answer = await fetchAnswer(questionId, sequenceNumber);
|
||||||
|
|
||||||
|
return answer.author.username == auth.username;
|
||||||
|
}
|
||||||
|
);
|
|
@ -1,16 +1,19 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { createAnswerHandler, deleteAnswerHandler, getAnswerHandler, getAllAnswersHandler, updateAnswerHandler } from '../controllers/answers.js';
|
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";
|
||||||
|
|
||||||
|
|
||||||
const router = express.Router({ mergeParams: true });
|
const router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
router.get('/', getAllAnswersHandler);
|
router.get('/', adminOnly, getAllAnswersHandler);
|
||||||
|
|
||||||
router.post('/', createAnswerHandler);
|
router.post('/', teachersOnly, onlyAllowAuthor, createAnswerHandler);
|
||||||
|
|
||||||
router.get('/:seqAnswer', getAnswerHandler);
|
router.get('/:seqAnswer', authenticatedOnly, getAnswerHandler);
|
||||||
|
|
||||||
router.delete('/:seqAnswer', deleteAnswerHandler);
|
router.delete('/:seqAnswer', teachersOnly, onlyAllowAuthorRequestAnswer, deleteAnswerHandler);
|
||||||
|
|
||||||
router.put('/:seqAnswer', updateAnswerHandler);
|
router.put('/:seqAnswer', teachersOnly, onlyAllowAuthorRequestAnswer, updateAnswerHandler);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { createQuestionHandler, deleteQuestionHandler, getAllQuestionsHandler, getQuestionHandler } from '../controllers/questions.js';
|
import { createQuestionHandler, deleteQuestionHandler, getAllQuestionsHandler, getQuestionHandler } from '../controllers/questions.js';
|
||||||
import answerRoutes from './answers.js';
|
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";
|
||||||
|
|
||||||
const router = express.Router({ mergeParams: true });
|
const router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
// Query language
|
// Query language
|
||||||
|
|
||||||
// Root endpoint used to search objects
|
// Root endpoint used to search objects
|
||||||
router.get('/', getAllQuestionsHandler);
|
router.get('/', adminOnly, getAllQuestionsHandler);
|
||||||
|
|
||||||
router.post('/', createQuestionHandler);
|
router.post('/', studentsOnly, onlyAllowAuthor, createQuestionHandler);
|
||||||
|
|
||||||
router.delete('/:seq', deleteQuestionHandler);
|
|
||||||
|
|
||||||
// Information about a question with id
|
// Information about a question with id
|
||||||
router.get('/:seq', getQuestionHandler);
|
router.get('/:seq', authenticatedOnly, getQuestionHandler); // TODO every body in group + teachers?
|
||||||
|
|
||||||
|
router.delete('/:seq', studentsOnly, onlyAllowAuthorRequest, deleteQuestionHandler);
|
||||||
|
|
||||||
|
router.put("/:seq", studentsOnly, onlyAllowAuthorRequest, updateAnswerHandler);
|
||||||
|
|
||||||
router.use('/:seq/answers', answerRoutes);
|
router.use('/:seq/answers', answerRoutes);
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ export async function createAnswer(questionId: QuestionId, answerData: AnswerDat
|
||||||
return mapToAnswerDTO(answer);
|
return mapToAnswerDTO(answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAnswer(questionId: QuestionId, sequenceNumber: number): Promise<Answer> {
|
export async function fetchAnswer(questionId: QuestionId, sequenceNumber: number): Promise<Answer> {
|
||||||
const answerRepository = getAnswerRepository();
|
const answerRepository = getAnswerRepository();
|
||||||
const question = await fetchQuestion(questionId);
|
const question = await fetchQuestion(questionId);
|
||||||
const answer = await answerRepository.findAnswer(question, sequenceNumber);
|
const answer = await answerRepository.findAnswer(question, sequenceNumber);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue