style: fix linting issues met Prettier
This commit is contained in:
		
							parent
							
								
									7f670030a7
								
							
						
					
					
						commit
						0c47546814
					
				
					 30 changed files with 233 additions and 262 deletions
				
			
		|  | @ -2,10 +2,10 @@ import { UnauthorizedException } from '../exceptions/unauthorized-exception.js'; | |||
| import { getLogger } from '../logging/initalize.js'; | ||||
| import { AuthenticatedRequest } from '../middleware/auth/authenticated-request.js'; | ||||
| import { envVars, getEnvVar } from '../util/envVars.js'; | ||||
| import {createOrUpdateStudent, createStudent} from "../services/students"; | ||||
| import {AuthenticationInfo} from "../middleware/auth/authentication-info"; | ||||
| import {Request, Response} from "express"; | ||||
| import {createOrUpdateTeacher, createTeacher} from "../services/teachers"; | ||||
| import { createOrUpdateStudent, createStudent } from '../services/students'; | ||||
| import { AuthenticationInfo } from '../middleware/auth/authentication-info'; | ||||
| import { Request, Response } from 'express'; | ||||
| import { createOrUpdateTeacher, createTeacher } from '../services/teachers'; | ||||
| 
 | ||||
| interface FrontendIdpConfig { | ||||
|     authority: string; | ||||
|  | @ -47,20 +47,26 @@ export function handleGetFrontendAuthConfig(_req: Request, res: Response): void | |||
| 
 | ||||
| export async function handleHello(req: AuthenticatedRequest): Promise<void> { | ||||
|     const auth: AuthenticationInfo = req.auth!; | ||||
|     if (auth.accountType === "teacher") { | ||||
|         await createTeacher({ | ||||
|             id: auth.username, | ||||
|             username: auth.username, | ||||
|             firstName: auth.firstName ?? "", | ||||
|             lastName: auth.lastName ?? "", | ||||
|         }, true); | ||||
|     if (auth.accountType === 'teacher') { | ||||
|         await createTeacher( | ||||
|             { | ||||
|                 id: auth.username, | ||||
|                 username: auth.username, | ||||
|                 firstName: auth.firstName ?? '', | ||||
|                 lastName: auth.lastName ?? '', | ||||
|             }, | ||||
|             true | ||||
|         ); | ||||
|     } else { | ||||
|         await createStudent({ | ||||
|             id: auth.username, | ||||
|             username: auth.username, | ||||
|             firstName: auth.firstName ?? "", | ||||
|             lastName: auth.lastName ?? "", | ||||
|         }, true); | ||||
|         await createStudent( | ||||
|             { | ||||
|                 id: auth.username, | ||||
|                 username: auth.username, | ||||
|                 firstName: auth.firstName ?? '', | ||||
|                 lastName: auth.lastName ?? '', | ||||
|             }, | ||||
|             true | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { Request, Response } from 'express'; | |||
| import { requireFields } from './error-helper.js'; | ||||
| import { createInvitation, deleteInvitation, getAllInvitations, getInvitation, updateInvitation } from '../services/teacher-invitations.js'; | ||||
| import { TeacherInvitationData } from '@dwengo-1/common/interfaces/teacher-invitation'; | ||||
| import {ConflictException} from "../exceptions/conflict-exception"; | ||||
| import { ConflictException } from '../exceptions/conflict-exception'; | ||||
| 
 | ||||
| export async function getAllInvitationsHandler(req: Request, res: Response): Promise<void> { | ||||
|     const username = req.params.username; | ||||
|  | @ -31,8 +31,8 @@ export async function createInvitationHandler(req: Request, res: Response): Prom | |||
|     const classId = req.body.class; | ||||
|     requireFields({ sender, receiver, classId }); | ||||
| 
 | ||||
|     if (sender === receiver){ | ||||
|         throw new ConflictException("Cannot send an invitation to yourself"); | ||||
|     if (sender === receiver) { | ||||
|         throw new ConflictException('Cannot send an invitation to yourself'); | ||||
|     } | ||||
| 
 | ||||
|     const data = req.body as TeacherInvitationData; | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ export function mapToUserDTO(user: User): UserDTO { | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| export function mapToUsername(user: {username: string}): string { | ||||
| export function mapToUsername(user: { username: string }): string { | ||||
|     return user.username; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,11 +1,11 @@ | |||
| import {envVars, getEnvVar} from '../../util/envVars.js'; | ||||
| import {expressjwt} from 'express-jwt'; | ||||
| import { envVars, getEnvVar } from '../../util/envVars.js'; | ||||
| import { expressjwt } from 'express-jwt'; | ||||
| import * as jwt from 'jsonwebtoken'; | ||||
| import {JwtPayload} from 'jsonwebtoken'; | ||||
| import { JwtPayload } from 'jsonwebtoken'; | ||||
| import jwksClient from 'jwks-rsa'; | ||||
| import * as express from 'express'; | ||||
| import {AuthenticatedRequest} from './authenticated-request.js'; | ||||
| import {AuthenticationInfo} from './authentication-info.js'; | ||||
| import { AuthenticatedRequest } from './authenticated-request.js'; | ||||
| import { AuthenticationInfo } from './authentication-info.js'; | ||||
| import { UnauthorizedException } from '../../exceptions/unauthorized-exception.js'; | ||||
| 
 | ||||
| const JWKS_CACHE = true; | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { Request } from 'express'; | ||||
| import { JwtPayload } from 'jsonwebtoken'; | ||||
| import { AuthenticationInfo } from './authentication-info.js'; | ||||
| import * as core from "express-serve-static-core"; | ||||
| import * as core from 'express-serve-static-core'; | ||||
| 
 | ||||
| export interface AuthenticatedRequest< | ||||
|     P = core.ParamsDictionary, | ||||
|  | @ -9,7 +9,7 @@ export interface AuthenticatedRequest< | |||
|     ReqBody = unknown, | ||||
|     ReqQuery = core.Query, | ||||
|     Locals extends Record<string, unknown> = Record<string, unknown>, | ||||
| > extends Request<P,ResBody,ReqBody,ReqQuery,Locals> { | ||||
| > extends Request<P, ResBody, ReqBody, ReqQuery, Locals> { | ||||
|     // Properties are optional since the user is not necessarily authenticated.
 | ||||
|     jwtPayload?: JwtPayload; | ||||
|     auth?: AuthenticationInfo; | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import {authorize} from "./auth-checks"; | ||||
| import {fetchClass} from "../../../services/classes"; | ||||
| import {fetchAllGroups} from "../../../services/groups"; | ||||
| import {mapToUsername} from "../../../interfaces/user"; | ||||
| import { authorize } from './auth-checks'; | ||||
| import { fetchClass } from '../../../services/classes'; | ||||
| import { fetchAllGroups } from '../../../services/groups'; | ||||
| import { mapToUsername } from '../../../interfaces/user'; | ||||
| 
 | ||||
| /** | ||||
|  * Expects the path to contain the path parameters 'classId' and 'id' (meaning the ID of the assignment). | ||||
|  | @ -9,15 +9,12 @@ import {mapToUsername} from "../../../interfaces/user"; | |||
|  * - either teachers of the class the assignment was posted in, | ||||
|  * - or students in a group of the assignment. | ||||
|  */ | ||||
| export const onlyAllowIfHasAccessToAssignment = authorize( | ||||
|     async (auth, req) => { | ||||
|         const { classid: classId, id: assignmentId } = req.params as { classid: string, id: number }; | ||||
|         if (auth.accountType === "teacher") { | ||||
|             const clazz = await fetchClass(classId); | ||||
|             return clazz.teachers.map(mapToUsername).includes(auth.username); | ||||
|         }  | ||||
|             const groups = await fetchAllGroups(classId, assignmentId); | ||||
|             return groups.some(group => group.members.map((member) => member.username).includes(auth.username) ); | ||||
|          | ||||
| export const onlyAllowIfHasAccessToAssignment = authorize(async (auth, req) => { | ||||
|     const { classid: classId, id: assignmentId } = req.params as { classid: string; id: number }; | ||||
|     if (auth.accountType === 'teacher') { | ||||
|         const clazz = await fetchClass(classId); | ||||
|         return clazz.teachers.map(mapToUsername).includes(auth.username); | ||||
|     } | ||||
| ); | ||||
|     const groups = await fetchAllGroups(classId, assignmentId); | ||||
|     return groups.some((group) => group.members.map((member) => member.username).includes(auth.username)); | ||||
| }); | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| import {AuthenticationInfo} from "../authentication-info"; | ||||
| import {AuthenticatedRequest} from "../authenticated-request"; | ||||
| import * as express from "express"; | ||||
| import {UnauthorizedException} from "../../../exceptions/unauthorized-exception"; | ||||
| import {ForbiddenException} from "../../../exceptions/forbidden-exception"; | ||||
| import {RequestHandler} from "express"; | ||||
| import { AuthenticationInfo } from '../authentication-info'; | ||||
| import { AuthenticatedRequest } from '../authenticated-request'; | ||||
| import * as express from 'express'; | ||||
| import { UnauthorizedException } from '../../../exceptions/unauthorized-exception'; | ||||
| import { ForbiddenException } from '../../../exceptions/forbidden-exception'; | ||||
| import { RequestHandler } from 'express'; | ||||
| 
 | ||||
| /** | ||||
|  * Middleware which rejects unauthenticated users (with HTTP 401) and authenticated users which do not fulfill | ||||
|  | @ -11,13 +11,17 @@ import {RequestHandler} from "express"; | |||
|  * @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates | ||||
|  *                        to true. | ||||
|  */ | ||||
| export function authorize<P,ResBody,ReqBody,ReqQuery,Locals extends Record<string, unknown>>( | ||||
|     accessCondition: (auth: AuthenticationInfo, req: AuthenticatedRequest<P,ResBody,ReqBody,ReqQuery,Locals>) => boolean | Promise<boolean> | ||||
| ): RequestHandler<P,ResBody,ReqBody,ReqQuery,Locals> { | ||||
|     return async (req: AuthenticatedRequest<P,ResBody,ReqBody,ReqQuery,Locals>, _res: express.Response, next: express.NextFunction): Promise<void> => { | ||||
| export function authorize<P, ResBody, ReqBody, ReqQuery, Locals extends Record<string, unknown>>( | ||||
|     accessCondition: (auth: AuthenticationInfo, req: AuthenticatedRequest<P, ResBody, ReqBody, ReqQuery, Locals>) => boolean | Promise<boolean> | ||||
| ): RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> { | ||||
|     return async ( | ||||
|         req: AuthenticatedRequest<P, ResBody, ReqBody, ReqQuery, Locals>, | ||||
|         _res: express.Response, | ||||
|         next: express.NextFunction | ||||
|     ): Promise<void> => { | ||||
|         if (!req.auth) { | ||||
|             throw new UnauthorizedException(); | ||||
|         } else if (!await accessCondition(req.auth, req)) { | ||||
|         } else if (!(await accessCondition(req.auth, req))) { | ||||
|             throw new ForbiddenException(); | ||||
|         } else { | ||||
|             next(); | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| import {authorize} from "./auth-checks"; | ||||
| import {AuthenticationInfo} from "../authentication-info"; | ||||
| import {AuthenticatedRequest} from "../authenticated-request"; | ||||
| import {fetchClass} from "../../../services/classes"; | ||||
| import {mapToUsername} from "../../../interfaces/user"; | ||||
| import { authorize } from './auth-checks'; | ||||
| import { AuthenticationInfo } from '../authentication-info'; | ||||
| import { AuthenticatedRequest } from '../authenticated-request'; | ||||
| import { fetchClass } from '../../../services/classes'; | ||||
| import { mapToUsername } from '../../../interfaces/user'; | ||||
| 
 | ||||
| async function teaches(teacherUsername: string, classId: string): Promise<boolean> { | ||||
|     const clazz = await fetchClass(classId); | ||||
|  | @ -14,53 +14,45 @@ async function teaches(teacherUsername: string, classId: string): Promise<boolea | |||
|  * Only allows requests whose username parameter is equal to the username of the user who is logged in and requests | ||||
|  * whose classId parameter references a class the logged-in user is a teacher of. | ||||
|  */ | ||||
| export const onlyAllowStudentHimselfAndTeachersOfClass = authorize( | ||||
|     async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | ||||
|         if (req.params.username === auth.username) { | ||||
|             return true; | ||||
|         } else if (auth.accountType === "teacher") { | ||||
|             return teaches(auth.username, req.params.classId); | ||||
|         } | ||||
|         return false; | ||||
| 
 | ||||
| export const onlyAllowStudentHimselfAndTeachersOfClass = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | ||||
|     if (req.params.username === auth.username) { | ||||
|         return true; | ||||
|     } else if (auth.accountType === 'teacher') { | ||||
|         return teaches(auth.username, req.params.classId); | ||||
|     } | ||||
| ); | ||||
|     return false; | ||||
| }); | ||||
| 
 | ||||
| /** | ||||
|  * Only let the request pass through if its path parameter "username" is the username of the currently logged-in | ||||
|  * teacher and the path parameter "classId" refers to a class the teacher teaches. | ||||
|  */ | ||||
| export const onlyAllowTeacherOfClass = authorize( | ||||
|     async (auth: AuthenticationInfo, req: AuthenticatedRequest) => | ||||
|         req.params.username === auth.username && teaches(auth.username, req.params.classId), | ||||
|     async (auth: AuthenticationInfo, req: AuthenticatedRequest) => req.params.username === auth.username && teaches(auth.username, req.params.classId) | ||||
| ); | ||||
| 
 | ||||
| /** | ||||
|  * Only let the request pass through if the class id in it refers to a class the current user is in (as a student | ||||
|  * or teacher) | ||||
|  */ | ||||
| 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") { | ||||
|             return clazz.teachers.map(mapToUsername).includes(auth.username); | ||||
|         } | ||||
|         return clazz.students.map(mapToUsername).includes(auth.username); | ||||
| 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') { | ||||
|         return clazz.teachers.map(mapToUsername).includes(auth.username); | ||||
|     } | ||||
| ); | ||||
|     return clazz.students.map(mapToUsername).includes(auth.username); | ||||
| }); | ||||
| 
 | ||||
| /** | ||||
|  * Only allows the request to pass if the 'class' property in its body is a class the current user is a member of. | ||||
|  */ | ||||
| export const onlyAllowOwnClassInBody = authorize( | ||||
|     async (auth, req) => { | ||||
|         const classId = (req.body as {class: string})?.class; | ||||
|         const clazz = await fetchClass(classId); | ||||
| export const onlyAllowOwnClassInBody = authorize(async (auth, req) => { | ||||
|     const classId = (req.body as { class: string })?.class; | ||||
|     const clazz = await fetchClass(classId); | ||||
| 
 | ||||
|         if (auth.accountType === "teacher") { | ||||
|             return clazz.teachers.map(mapToUsername).includes(auth.username); | ||||
|         } | ||||
|         return clazz.students.map(mapToUsername).includes(auth.username); | ||||
|     if (auth.accountType === 'teacher') { | ||||
|         return clazz.teachers.map(mapToUsername).includes(auth.username); | ||||
|     } | ||||
| ); | ||||
|     return clazz.students.map(mapToUsername).includes(auth.username); | ||||
| }); | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import {authorize} from "./auth-checks"; | ||||
| import {fetchClass} from "../../../services/classes"; | ||||
| import {fetchGroup} from "../../../services/groups"; | ||||
| import {mapToUsername} from "../../../interfaces/user"; | ||||
| import { authorize } from './auth-checks'; | ||||
| import { fetchClass } from '../../../services/classes'; | ||||
| import { fetchGroup } from '../../../services/groups'; | ||||
| import { mapToUsername } from '../../../interfaces/user'; | ||||
| 
 | ||||
| /** | ||||
|  * Expects the path to contain the path parameters 'classid', 'assignmentid' and 'groupid'. | ||||
|  | @ -9,17 +9,17 @@ import {mapToUsername} from "../../../interfaces/user"; | |||
|  * - either teachers of the class the assignment for the group was posted in, | ||||
|  * - or students in the group | ||||
|  */ | ||||
| export const onlyAllowIfHasAccessToGroup = authorize( | ||||
|     async (auth, req) => { | ||||
|         const { classid: classId, assignmentid: assignmentId, groupid: groupId } = | ||||
|             req.params as { classid: string, assignmentid: number, groupid: number }; | ||||
| export const onlyAllowIfHasAccessToGroup = authorize(async (auth, req) => { | ||||
|     const { | ||||
|         classid: classId, | ||||
|         assignmentid: assignmentId, | ||||
|         groupid: groupId, | ||||
|     } = req.params as { classid: string; assignmentid: number; groupid: number }; | ||||
| 
 | ||||
|         if (auth.accountType === "teacher") { | ||||
|             const clazz = await fetchClass(classId); | ||||
|             return clazz.teachers.map(mapToUsername).includes(auth.username); | ||||
|         }  // User is student
 | ||||
|             const group = await fetchGroup(classId, assignmentId, groupId); | ||||
|             return group.members.map(mapToUsername).includes(auth.username); | ||||
|          | ||||
|     } | ||||
| ); | ||||
|     if (auth.accountType === 'teacher') { | ||||
|         const clazz = await fetchClass(classId); | ||||
|         return clazz.teachers.map(mapToUsername).includes(auth.username); | ||||
|     } // User is student
 | ||||
|     const group = await fetchGroup(classId, assignmentId, groupId); | ||||
|     return group.members.map(mapToUsername).includes(auth.username); | ||||
| }); | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import {authorize} from "./auth-checks"; | ||||
| import {AuthenticationInfo} from "../authentication-info"; | ||||
| import {AuthenticatedRequest} from "../authenticated-request"; | ||||
| import { authorize } from './auth-checks'; | ||||
| import { AuthenticationInfo } from '../authentication-info'; | ||||
| import { AuthenticatedRequest } from '../authenticated-request'; | ||||
| 
 | ||||
| /** | ||||
|  * Only allows requests whose learning path personalization query parameters ('forGroup' / 'assignmentNo' / 'classId') | ||||
|  | @ -9,15 +9,12 @@ import {AuthenticatedRequest} from "../authenticated-request"; | |||
|  * - or set to a group the user is in, | ||||
|  * - or set to anything if the user is a teacher. | ||||
|  */ | ||||
| export const onlyAllowPersonalizationForOwnGroup = authorize( | ||||
|     async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | ||||
|         const {forGroup, assignmentNo, classId} = req.params; | ||||
|         if (auth.accountType === "student" && forGroup && assignmentNo && classId) { | ||||
|             // TODO: groupNumber? 
 | ||||
|             // Const group = await fetchGroup(Number(classId), Number(assignmentNo), )
 | ||||
|             return false; | ||||
|         }  | ||||
|             return true; | ||||
|          | ||||
| export const onlyAllowPersonalizationForOwnGroup = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | ||||
|     const { forGroup, assignmentNo, classId } = req.params; | ||||
|     if (auth.accountType === 'student' && forGroup && assignmentNo && classId) { | ||||
|         // TODO: groupNumber?
 | ||||
|         // Const group = await fetchGroup(Number(classId), Number(assignmentNo), )
 | ||||
|         return false; | ||||
|     } | ||||
| ); | ||||
|     return true; | ||||
| }); | ||||
|  |  | |||
|  | @ -1,72 +1,65 @@ | |||
| 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"; | ||||
| import {mapToUsername} from "../../../interfaces/user"; | ||||
| 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'; | ||||
| import { mapToUsername } from '../../../interfaces/user'; | ||||
| 
 | ||||
| export const onlyAllowAuthor = authorize( | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => (req.body as { author: string }).author === auth.username | ||||
| ); | ||||
| 
 | ||||
| export const onlyAllowAuthorRequest = authorize( | ||||
|     async (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 }); | ||||
| export const onlyAllowAuthorRequest = authorize(async (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 learningObjectId = getLearningObjectId(hruid, version, language); | ||||
|     const questionId = getQuestionId(learningObjectId, seq); | ||||
| 
 | ||||
|         const question = await fetchQuestion(questionId); | ||||
|     const question = await fetchQuestion(questionId); | ||||
| 
 | ||||
|         return question.author.username === auth.username; | ||||
|     } | ||||
| ); | ||||
|     return question.author.username === auth.username; | ||||
| }); | ||||
| 
 | ||||
| export const onlyAllowAuthorRequestAnswer = authorize( | ||||
|     async (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 }); | ||||
| export const onlyAllowAuthorRequestAnswer = authorize(async (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 learningObjectId = getLearningObjectId(hruid, version, language); | ||||
|     const questionId = getQuestionId(learningObjectId, seq); | ||||
| 
 | ||||
|         const sequenceNumber = Number(seqAnswer) || FALLBACK_SEQ_NUM; | ||||
|         const answer = await fetchAnswer(questionId, sequenceNumber); | ||||
|     const sequenceNumber = Number(seqAnswer) || FALLBACK_SEQ_NUM; | ||||
|     const answer = await fetchAnswer(questionId, sequenceNumber); | ||||
| 
 | ||||
|         return answer.author.username === auth.username; | ||||
|     } | ||||
| ); | ||||
|     return answer.author.username === auth.username; | ||||
| }); | ||||
| 
 | ||||
| export const onlyAllowIfHasAccessToQuestion = authorize( | ||||
|     async (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 }); | ||||
| export const onlyAllowIfHasAccessToQuestion = authorize(async (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 learningObjectId = getLearningObjectId(hruid, version, language); | ||||
|     const questionId = getQuestionId(learningObjectId, seq); | ||||
| 
 | ||||
|         const question = await fetchQuestion(questionId); | ||||
|         const group = question.inGroup; | ||||
|     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); | ||||
|         }  // User is student
 | ||||
|             return group.members.map(mapToUsername).includes(auth.username); | ||||
|          | ||||
|     } | ||||
| ); | ||||
|     if (auth.accountType === 'teacher') { | ||||
|         const cls = group.assignment.within; // TODO check if contains full objects
 | ||||
|         return cls.teachers.map(mapToUsername).includes(auth.username); | ||||
|     } // User is student
 | ||||
|     return group.members.map(mapToUsername).includes(auth.username); | ||||
| }); | ||||
|  |  | |||
|  | @ -1,29 +1,27 @@ | |||
| import { languageMap } from "dwengo-1-common/util/language"; | ||||
| import { LearningObjectIdentifier } from "../../../entities/content/learning-object-identifier"; | ||||
| import { fetchSubmission } from "../../../services/submissions"; | ||||
| import { AuthenticatedRequest } from "../authenticated-request"; | ||||
| import { AuthenticationInfo } from "../authentication-info"; | ||||
| import { authorize } from "./auth-checks"; | ||||
| import { FALLBACK_LANG } from "../../../config"; | ||||
| import { mapToUsername } from "../../../interfaces/user"; | ||||
| import { languageMap } from 'dwengo-1-common/util/language'; | ||||
| import { LearningObjectIdentifier } from '../../../entities/content/learning-object-identifier'; | ||||
| import { fetchSubmission } from '../../../services/submissions'; | ||||
| import { AuthenticatedRequest } from '../authenticated-request'; | ||||
| import { AuthenticationInfo } from '../authentication-info'; | ||||
| import { authorize } from './auth-checks'; | ||||
| import { FALLBACK_LANG } from '../../../config'; | ||||
| import { mapToUsername } from '../../../interfaces/user'; | ||||
| 
 | ||||
| export const onlyAllowSubmitter = authorize( | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => (req.body as { submitter: string }).submitter === auth.username | ||||
| ); | ||||
| 
 | ||||
| export const onlyAllowIfHasAccessToSubmission = authorize( | ||||
|     async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | ||||
|         const { hruid: lohruid, id: submissionNumber } = req.params; | ||||
|         const { language: lang, version: version } = req.query; | ||||
| export const onlyAllowIfHasAccessToSubmission = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => { | ||||
|     const { hruid: lohruid, id: submissionNumber } = req.params; | ||||
|     const { language: lang, version: version } = req.query; | ||||
| 
 | ||||
|         const loId = new LearningObjectIdentifier(lohruid,  languageMap[lang as string] ?? FALLBACK_LANG, Number(version)) | ||||
|         const submission = await fetchSubmission(loId, Number(submissionNumber)); | ||||
|     const loId = new LearningObjectIdentifier(lohruid, languageMap[lang as string] ?? FALLBACK_LANG, Number(version)); | ||||
|     const submission = await fetchSubmission(loId, Number(submissionNumber)); | ||||
| 
 | ||||
|         if (auth.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); | ||||
|         }  | ||||
| 
 | ||||
|         return submission.onBehalfOf.members.map(mapToUsername).includes(auth.username); | ||||
|     if (auth.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); | ||||
|     } | ||||
| ) | ||||
| 
 | ||||
|     return submission.onBehalfOf.members.map(mapToUsername).includes(auth.username); | ||||
| }); | ||||
|  |  | |||
|  | @ -1,23 +1,17 @@ | |||
| import {authorize} from "./auth-checks"; | ||||
| import {AuthenticationInfo} from "../authentication-info"; | ||||
| import {AuthenticatedRequest} from "../authenticated-request"; | ||||
| import { authorize } from './auth-checks'; | ||||
| import { AuthenticationInfo } from '../authentication-info'; | ||||
| import { AuthenticatedRequest } from '../authenticated-request'; | ||||
| 
 | ||||
| export const onlyAllowSenderOrReceiver = authorize( | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => | ||||
|         req.params.sender === auth.username || req.params.receiver === auth.username | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => req.params.sender === auth.username || req.params.receiver === auth.username | ||||
| ); | ||||
| 
 | ||||
| export const onlyAllowSender = authorize( | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => | ||||
|         req.params.sender === auth.username | ||||
| ); | ||||
| export const onlyAllowSender = authorize((auth: AuthenticationInfo, req: AuthenticatedRequest) => req.params.sender === auth.username); | ||||
| 
 | ||||
| export const onlyAllowSenderBody = authorize( | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => | ||||
|         (req.body as { sender: string }).sender === auth.username | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => (req.body as { sender: string }).sender === auth.username | ||||
| ); | ||||
| 
 | ||||
| export const onlyAllowReceiverBody = authorize( | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => | ||||
|         (req.body as { receiver: string }).receiver === auth.username | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => (req.body as { receiver: string }).receiver === auth.username | ||||
| ); | ||||
|  |  | |||
|  | @ -1,10 +1,8 @@ | |||
| import {authorize} from "./auth-checks"; | ||||
| import {AuthenticationInfo} from "../authentication-info"; | ||||
| import {AuthenticatedRequest} from "../authenticated-request"; | ||||
| import { authorize } from './auth-checks'; | ||||
| import { AuthenticationInfo } from '../authentication-info'; | ||||
| import { AuthenticatedRequest } from '../authenticated-request'; | ||||
| 
 | ||||
| /** | ||||
|  * Only allow the user whose username is in the path parameter "username" to access the endpoint. | ||||
|  */ | ||||
| export const onlyAllowUserHimself = authorize( | ||||
|     (auth: AuthenticationInfo, req: AuthenticatedRequest) => req.params.username === auth.username | ||||
| ); | ||||
| export const onlyAllowUserHimself = authorize((auth: AuthenticationInfo, req: AuthenticatedRequest) => req.params.username === auth.username); | ||||
|  |  | |||
|  | @ -1,12 +1,7 @@ | |||
| import express from 'express'; | ||||
| import { createAnswerHandler, deleteAnswerHandler, getAnswerHandler, getAllAnswersHandler, updateAnswerHandler } from '../controllers/answers.js'; | ||||
| import {adminOnly, teachersOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import { | ||||
|     onlyAllowAuthor, | ||||
|     onlyAllowAuthorRequestAnswer, | ||||
|     onlyAllowIfHasAccessToQuestion | ||||
| } from "../middleware/auth/checks/question-checks"; | ||||
| 
 | ||||
| import { adminOnly, teachersOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| import { onlyAllowAuthor, onlyAllowAuthorRequestAnswer, onlyAllowIfHasAccessToQuestion } from '../middleware/auth/checks/question-checks'; | ||||
| 
 | ||||
| const router = express.Router({ mergeParams: true }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,9 +8,9 @@ import { | |||
|     putAssignmentHandler, | ||||
| } from '../controllers/assignments.js'; | ||||
| import groupRouter from './groups.js'; | ||||
| import {teachersOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import {onlyAllowIfInClass} from "../middleware/auth/checks/class-auth-checks"; | ||||
| import {onlyAllowIfHasAccessToAssignment} from "../middleware/auth/checks/assignment-auth-checks"; | ||||
| import { teachersOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| import { onlyAllowIfInClass } from '../middleware/auth/checks/class-auth-checks'; | ||||
| import { onlyAllowIfHasAccessToAssignment } from '../middleware/auth/checks/assignment-auth-checks'; | ||||
| 
 | ||||
| const router = express.Router({ mergeParams: true }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,8 +14,8 @@ import { | |||
|     putClassHandler, | ||||
| } from '../controllers/classes.js'; | ||||
| import assignmentRouter from './assignments.js'; | ||||
| import {adminOnly, teachersOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import {onlyAllowIfInClass} from "../middleware/auth/checks/class-auth-checks"; | ||||
| import { adminOnly, teachersOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| import { onlyAllowIfInClass } from '../middleware/auth/checks/class-auth-checks'; | ||||
| 
 | ||||
| const router = express.Router(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,9 +7,9 @@ import { | |||
|     getGroupSubmissionsHandler, | ||||
|     putGroupHandler, | ||||
| } from '../controllers/groups.js'; | ||||
| import {onlyAllowIfHasAccessToGroup} from "../middleware/auth/checks/group-auth-checker"; | ||||
| import {teachersOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import {onlyAllowIfHasAccessToAssignment} from "../middleware/auth/checks/assignment-auth-checks"; | ||||
| import { onlyAllowIfHasAccessToGroup } from '../middleware/auth/checks/group-auth-checker'; | ||||
| import { teachersOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| import { onlyAllowIfHasAccessToAssignment } from '../middleware/auth/checks/assignment-auth-checks'; | ||||
| 
 | ||||
| const router = express.Router({ mergeParams: true }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import { getAllLearningObjects, getAttachment, getLearningObject, getLearningObj | |||
| 
 | ||||
| import submissionRoutes from './submissions.js'; | ||||
| import questionRoutes from './questions.js'; | ||||
| import {authenticatedOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import { authenticatedOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| 
 | ||||
| const router = express.Router(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import express from 'express'; | ||||
| import { getLearningPaths } from '../controllers/learning-paths.js'; | ||||
| import {authenticatedOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import { authenticatedOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| 
 | ||||
| const router = express.Router(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,13 +1,9 @@ | |||
| import express from 'express'; | ||||
| import { createQuestionHandler, deleteQuestionHandler, getAllQuestionsHandler, getQuestionHandler } from '../controllers/questions.js'; | ||||
| import answerRoutes from './answers.js'; | ||||
| import {adminOnly, studentsOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import {updateAnswerHandler} from "../controllers/answers"; | ||||
| import { | ||||
|     onlyAllowAuthor, | ||||
|     onlyAllowAuthorRequest, | ||||
|     onlyAllowIfHasAccessToQuestion | ||||
| } from "../middleware/auth/checks/question-checks"; | ||||
| import { adminOnly, studentsOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| import { updateAnswerHandler } from '../controllers/answers'; | ||||
| import { onlyAllowAuthor, onlyAllowAuthorRequest, onlyAllowIfHasAccessToQuestion } from '../middleware/auth/checks/question-checks'; | ||||
| 
 | ||||
| const router = express.Router({ mergeParams: true }); | ||||
| 
 | ||||
|  | @ -23,7 +19,7 @@ router.get('/:seq', onlyAllowIfHasAccessToQuestion, getQuestionHandler); | |||
| 
 | ||||
| router.delete('/:seq', studentsOnly, onlyAllowAuthorRequest, deleteQuestionHandler); | ||||
| 
 | ||||
| router.put("/:seq", studentsOnly, onlyAllowAuthorRequest, updateAnswerHandler); | ||||
| router.put('/:seq', studentsOnly, onlyAllowAuthorRequest, updateAnswerHandler); | ||||
| 
 | ||||
| router.use('/:seq/answers', answerRoutes); | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,8 +5,8 @@ import { | |||
|     getStudentRequestHandler, | ||||
|     getStudentRequestsHandler, | ||||
| } from '../controllers/students.js'; | ||||
| import {onlyAllowUserHimself} from "../middleware/auth/checks/user-auth-checks"; | ||||
| import {onlyAllowStudentHimselfAndTeachersOfClass} from "../middleware/auth/checks/class-auth-checks"; | ||||
| import { onlyAllowUserHimself } from '../middleware/auth/checks/user-auth-checks'; | ||||
| import { onlyAllowStudentHimselfAndTeachersOfClass } from '../middleware/auth/checks/class-auth-checks'; | ||||
| 
 | ||||
| // Under /:username/joinRequests/
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,8 +11,8 @@ import { | |||
|     getStudentSubmissionsHandler, | ||||
| } from '../controllers/students.js'; | ||||
| import joinRequestRouter from './student-join-requests.js'; | ||||
| import {onlyAllowUserHimself} from "../middleware/auth/checks/user-auth-checks"; | ||||
| import {adminOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import { onlyAllowUserHimself } from '../middleware/auth/checks/user-auth-checks'; | ||||
| import { adminOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| 
 | ||||
| const router = express.Router(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,18 +6,19 @@ import { | |||
|     getInvitationHandler, | ||||
|     updateInvitationHandler, | ||||
| } from '../controllers/teacher-invitations'; | ||||
| import {onlyAllowUserHimself} from "../middleware/auth/checks/user-auth-checks"; | ||||
| import { onlyAllowUserHimself } from '../middleware/auth/checks/user-auth-checks'; | ||||
| import { | ||||
|     onlyAllowReceiverBody, onlyAllowSender, | ||||
|     onlyAllowReceiverBody, | ||||
|     onlyAllowSender, | ||||
|     onlyAllowSenderBody, | ||||
|     onlyAllowSenderOrReceiver | ||||
| } from "../middleware/auth/checks/teacher-invitation-checks"; | ||||
|     onlyAllowSenderOrReceiver, | ||||
| } from '../middleware/auth/checks/teacher-invitation-checks'; | ||||
| 
 | ||||
| const router = express.Router({ mergeParams: true }); | ||||
| 
 | ||||
| router.get('/:username', onlyAllowUserHimself, getAllInvitationsHandler); | ||||
| 
 | ||||
| router.get('/:sender/:receiver/:classId', onlyAllowSenderOrReceiver ,getInvitationHandler); | ||||
| router.get('/:sender/:receiver/:classId', onlyAllowSenderOrReceiver, getInvitationHandler); | ||||
| 
 | ||||
| router.post('/', onlyAllowSenderBody, createInvitationHandler); | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,9 +12,9 @@ import { | |||
| } from '../controllers/teachers.js'; | ||||
| import invitationRouter from './teacher-invitations.js'; | ||||
| 
 | ||||
| import {adminOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import {onlyAllowUserHimself} from "../middleware/auth/checks/user-auth-checks"; | ||||
| import {onlyAllowTeacherOfClass} from "../middleware/auth/checks/class-auth-checks"; | ||||
| import { adminOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| import { onlyAllowUserHimself } from '../middleware/auth/checks/user-auth-checks'; | ||||
| import { onlyAllowTeacherOfClass } from '../middleware/auth/checks/class-auth-checks'; | ||||
| const router = express.Router(); | ||||
| 
 | ||||
| // Root endpoint used to search objects
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import express from 'express'; | ||||
| import { getThemesHandler, getHruidsByThemeHandler } from '../controllers/themes.js'; | ||||
| import {authenticatedOnly} from "../middleware/auth/checks/auth-checks"; | ||||
| import { authenticatedOnly } from '../middleware/auth/checks/auth-checks'; | ||||
| 
 | ||||
| const router = express.Router(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment'; | |||
| import { fetchStudent } from './students.js'; | ||||
| import { NotFoundException } from '../exceptions/not-found-exception.js'; | ||||
| import { FALLBACK_VERSION_NUM } from '../config.js'; | ||||
| import {ConflictException} from "../exceptions/conflict-exception"; | ||||
| import { ConflictException } from '../exceptions/conflict-exception'; | ||||
| 
 | ||||
| export async function getQuestionsAboutLearningObjectInAssignment( | ||||
|     loId: LearningObjectIdentifier, | ||||
|  | @ -91,12 +91,12 @@ export async function createQuestion(loId: LearningObjectIdentifier, questionDat | |||
|     const assignment = mapToAssignment(questionData.inGroup.assignment as AssignmentDTO, clazz!); | ||||
|     const group = await getGroupRepository().findByAssignmentAndGroupNumber(assignment, questionData.inGroup.groupNumber); | ||||
| 
 | ||||
|     if (!group){ | ||||
|         throw new NotFoundException("Group with id and assignment not found"); | ||||
|     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"); | ||||
|     if (!group.members.contains(author)) { | ||||
|         throw new ConflictException('Author is not part of this group'); | ||||
|     } | ||||
| 
 | ||||
|     const question = await questionRepository.createQuestion({ | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question'; | |||
| import { ClassJoinRequestDTO } from '@dwengo-1/common/interfaces/class-join-request'; | ||||
| import { ConflictException } from '../exceptions/conflict-exception.js'; | ||||
| import { Submission } from '../entities/assignments/submission.entity'; | ||||
| import {mapToUsername} from "../interfaces/user"; | ||||
| import { mapToUsername } from '../interfaces/user'; | ||||
| 
 | ||||
| export async function getAllStudents(full: boolean): Promise<StudentDTO[] | string[]> { | ||||
|     const studentRepository = getStudentRepository(); | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ export async function createInvitation(data: TeacherInvitationData): Promise<Tea | |||
|         throw new ConflictException('The teacher sending the invite is not part of the class'); | ||||
|     } | ||||
| 
 | ||||
|     if (cls.teachers.contains(receiver)){ | ||||
|     if (cls.teachers.contains(receiver)) { | ||||
|         throw new ConflictException('The teacher receiving the invite is already part of the class'); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question'; | |||
| import { ClassJoinRequestDTO } from '@dwengo-1/common/interfaces/class-join-request'; | ||||
| import { ClassStatus } from '@dwengo-1/common/util/class-join-request'; | ||||
| import { ConflictException } from '../exceptions/conflict-exception.js'; | ||||
| import {mapToUsername} from "../interfaces/user"; | ||||
| import { mapToUsername } from '../interfaces/user'; | ||||
| 
 | ||||
| export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | string[]> { | ||||
|     const teacherRepository: TeacherRepository = getTeacherRepository(); | ||||
|  |  | |||
		Reference in a new issue
	
	 Lint Action
						Lint Action