feat(backend): Bescherming van leerobject-manipulatie endpoints.
Ook delete route voor leerobjecten toegevoegd.
This commit is contained in:
parent
a7f90aace3
commit
20c04370b5
4 changed files with 50 additions and 4 deletions
|
@ -8,6 +8,7 @@ import { AuthenticatedRequest } from './authenticated-request.js';
|
||||||
import { AuthenticationInfo } from './authentication-info.js';
|
import { AuthenticationInfo } from './authentication-info.js';
|
||||||
import { UnauthorizedException } from '../../exceptions/unauthorized-exception.js';
|
import { UnauthorizedException } from '../../exceptions/unauthorized-exception.js';
|
||||||
import { ForbiddenException } from '../../exceptions/forbidden-exception.js';
|
import { ForbiddenException } from '../../exceptions/forbidden-exception.js';
|
||||||
|
import { RequestHandler } from 'express';
|
||||||
|
|
||||||
const JWKS_CACHE = true;
|
const JWKS_CACHE = true;
|
||||||
const JWKS_RATE_LIMIT = true;
|
const JWKS_RATE_LIMIT = true;
|
||||||
|
@ -115,11 +116,17 @@ export const authenticateUser = [verifyJwtToken, addAuthenticationInfo];
|
||||||
* @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates
|
* @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates
|
||||||
* to true.
|
* to true.
|
||||||
*/
|
*/
|
||||||
export function authorize(accessCondition: (auth: AuthenticationInfo) => boolean) {
|
export function authorize(
|
||||||
return (req: AuthenticatedRequest, _res: express.Response, next: express.NextFunction): void => {
|
accessCondition: (auth: AuthenticationInfo, req: AuthenticatedRequest) => boolean | Promise<boolean>
|
||||||
|
): RequestHandler {
|
||||||
|
return async (
|
||||||
|
req: AuthenticatedRequest,
|
||||||
|
_res: express.Response,
|
||||||
|
next: express.NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
if (!req.auth) {
|
if (!req.auth) {
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
} else if (!accessCondition(req.auth)) {
|
} else if (!(await accessCondition(req.auth, req))) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Language } from "@dwengo-1/common/util/language";
|
||||||
|
import learningObjectService from "../../../services/learning-objects/learning-object-service";
|
||||||
|
import { authorize } from "../auth";
|
||||||
|
import { AuthenticatedRequest } from "../authenticated-request";
|
||||||
|
import { AuthenticationInfo } from "../authentication-info";
|
||||||
|
|
||||||
|
export const onlyAdminsForLearningObject = authorize(async (auth: AuthenticationInfo, req: AuthenticatedRequest) => {
|
||||||
|
const { hruid } = req.params;
|
||||||
|
const { version, language } = req.query;
|
||||||
|
const admins = await learningObjectService.getAdmins({
|
||||||
|
hruid,
|
||||||
|
language: language as Language,
|
||||||
|
version: parseInt(version as string)
|
||||||
|
});
|
||||||
|
return auth.username in admins;
|
||||||
|
});
|
|
@ -4,12 +4,15 @@ import {
|
||||||
getAttachment,
|
getAttachment,
|
||||||
getLearningObject,
|
getLearningObject,
|
||||||
getLearningObjectHTML,
|
getLearningObjectHTML,
|
||||||
|
handleDeleteLearningObject,
|
||||||
handlePostLearningObject
|
handlePostLearningObject
|
||||||
} from '../controllers/learning-objects.js';
|
} from '../controllers/learning-objects.js';
|
||||||
|
|
||||||
import submissionRoutes from './submissions.js';
|
import submissionRoutes from './submissions.js';
|
||||||
import questionRoutes from './questions.js';
|
import questionRoutes from './questions.js';
|
||||||
import fileUpload from "express-fileupload";
|
import fileUpload from "express-fileupload";
|
||||||
|
import { teachersOnly } from '../middleware/auth/auth.js';
|
||||||
|
import { onlyAdminsForLearningObject } from '../middleware/auth/checks/learning-object-auth-checks.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
@ -25,7 +28,7 @@ const router = express.Router();
|
||||||
// Example 2: http://localhost:3000/learningObject?full=true&hruid=un_artificiele_intelligentie
|
// Example 2: http://localhost:3000/learningObject?full=true&hruid=un_artificiele_intelligentie
|
||||||
router.get('/', getAllLearningObjects);
|
router.get('/', getAllLearningObjects);
|
||||||
|
|
||||||
router.post('/', fileUpload({useTempFiles: true}), handlePostLearningObject)
|
router.post('/', teachersOnly, fileUpload({useTempFiles: true}), handlePostLearningObject)
|
||||||
|
|
||||||
// Parameter: hruid of learning object
|
// Parameter: hruid of learning object
|
||||||
// Query: language
|
// Query: language
|
||||||
|
@ -33,6 +36,12 @@ router.post('/', fileUpload({useTempFiles: true}), handlePostLearningObject)
|
||||||
// Example: http://localhost:3000/learningObject/un_ai7
|
// Example: http://localhost:3000/learningObject/un_ai7
|
||||||
router.get('/:hruid', getLearningObject);
|
router.get('/:hruid', getLearningObject);
|
||||||
|
|
||||||
|
// Parameter: hruid of learning object
|
||||||
|
// Query: language
|
||||||
|
// Route to delete a learning object based on its hruid.
|
||||||
|
// Example: http://localhost:3000/learningObject/un_ai7?language=nl&version=1
|
||||||
|
router.delete('/:hruid', onlyAdminsForLearningObject, handleDeleteLearningObject)
|
||||||
|
|
||||||
router.use('/:hruid/submissions', submissionRoutes);
|
router.use('/:hruid/submissions', submissionRoutes);
|
||||||
|
|
||||||
router.use('/:hruid/:version/questions', questionRoutes);
|
router.use('/:hruid/:version/questions', questionRoutes);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {getLearningObjectRepository, getTeacherRepository} from "../../data/repo
|
||||||
import {processLearningObjectZip} from "./learning-object-zip-processing-service";
|
import {processLearningObjectZip} from "./learning-object-zip-processing-service";
|
||||||
import {LearningObject} from "../../entities/content/learning-object.entity";
|
import {LearningObject} from "../../entities/content/learning-object.entity";
|
||||||
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
|
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
|
||||||
|
import { NotFoundException } from '../../exceptions/not-found-exception.js';
|
||||||
|
|
||||||
function getProvider(id: LearningObjectIdentifierDTO): LearningObjectProvider {
|
function getProvider(id: LearningObjectIdentifierDTO): LearningObjectProvider {
|
||||||
if (id.hruid.startsWith(getEnvVar(envVars.UserContentPrefix))) {
|
if (id.hruid.startsWith(getEnvVar(envVars.UserContentPrefix))) {
|
||||||
|
@ -92,6 +93,19 @@ const learningObjectService = {
|
||||||
async deleteLearningObject(id: LearningObjectIdentifier): Promise<LearningObject | null> {
|
async deleteLearningObject(id: LearningObjectIdentifier): Promise<LearningObject | null> {
|
||||||
const learningObjectRepository = getLearningObjectRepository();
|
const learningObjectRepository = getLearningObjectRepository();
|
||||||
return await learningObjectRepository.removeByIdentifier(id);
|
return await learningObjectRepository.removeByIdentifier(id);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of the usernames of the administrators of the learning object with the given identifier.
|
||||||
|
* @throws NotFoundException if the specified learning object was not found in the database.
|
||||||
|
*/
|
||||||
|
async getAdmins(id: LearningObjectIdentifier): Promise<string[]> {
|
||||||
|
const learningObjectRepo = getLearningObjectRepository();
|
||||||
|
const learningObject = await learningObjectRepo.findByIdentifier(id);
|
||||||
|
if (!learningObject) {
|
||||||
|
throw new NotFoundException("The specified learning object does not exist.");
|
||||||
|
}
|
||||||
|
return learningObject.admins.map(admin => admin.username);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue