Skip to content

Commit

Permalink
centralize "isMentor" to middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
ngoerlitz committed May 19, 2024
1 parent 9919a86 commit cc35d78
Show file tree
Hide file tree
Showing 12 changed files with 68 additions and 51 deletions.
2 changes: 2 additions & 0 deletions backend/db/seeders/20221121101837-PermissionSeeder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const allPerms = [
"lm.endorsement_groups.edit",
"lm.endorsement_groups.create",

"lm.training_types.view",

"atd.view",
"atd.solo.delete",
"atd.examiner.view",
Expand Down
4 changes: 4 additions & 0 deletions backend/src/Router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import JoblogAdminController from "./controllers/admin-logs/JoblogAdminControlle
import UserInformationController from "./controllers/user/UserInformationController";
import CourseInformationAdministrationController from "./controllers/course/CourseInformationAdministrationController";
import UserAdminController from "./controllers/user/UserAdminController";
import { isMentorMiddleware } from "./middlewares/IsMentorMiddleware";

export const routerGroup = (callback: (router: Router) => void) => {
const router = Router();
Expand Down Expand Up @@ -172,6 +173,9 @@ router.use(
router.use(
"/administration",
routerGroup((r: Router) => {
// For all routes from this point onwards, a user MUST be at least a mentor.
r.use(isMentorMiddleware);

r.use(
"/user",
routerGroup((r: Router) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,8 @@ async function removeStationByID(request: Request, response: Response, next: Nex
*/
async function getUsersByID(request: Request, response: Response, next: NextFunction) {
try {
const user: User = response.locals.user;
const params = request.params as { id: string };

if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor.");
}

const endorsementGroup = await EndorsementGroup.findOne({
where: {
id: params.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,6 @@ async function create(request: Request, response: Response, next: NextFunction)
const user: User = response.locals.user;
const data = request.body;

if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor");
}

const file_name = handleUpload(request);

if (file_name == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ async function getAll(_request: Request, response: Response, next: NextFunction)
*/
async function getAllMinimalData(_request: Request, response: Response, next: NextFunction) {
try {
const user: User = response.locals.user;
if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor");
}

const logTemplates = await TrainingLogTemplate.findAll({
attributes: ["id", "name"],
});
Expand Down
23 changes: 3 additions & 20 deletions backend/src/controllers/mentor-group/MentorGroupAdminController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,6 @@ async function update(request: Request, response: Response, next: NextFunction)
*/
async function getAll(_request: Request, response: Response, next: NextFunction) {
try {
const user: User = response.locals.user;
if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor.");
}

const mentorGroups: MentorGroup[] = await MentorGroup.findAll({
include: {
association: MentorGroup.associations.users,
Expand Down Expand Up @@ -174,16 +169,16 @@ async function getByID(request: Request, response: Response, next: NextFunction)

/**
* Gets all mentor groups in which the response.locals.user is an admin in
* This is only used to display the mentor group admin list, so we can use the respective permission, rather than generically checking
* if the user is a mentor.
* @param _request
* @param response
* @param next
*/
async function getAllAdmin(_request: Request, response: Response, next: NextFunction) {
try {
const user: User = response.locals.user;
if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor.");
}
PermissionHelper.checkUserHasPermission(user, "lm.mentor_group.view");

const userInMentorGroup: UserBelongToMentorGroups[] = await UserBelongToMentorGroups.findAll({
where: {
Expand Down Expand Up @@ -216,10 +211,6 @@ async function getMembers(request: Request, response: Response, next: NextFuncti
const user: User = response.locals.user;
const query = request.query;

if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor");
}

// Check if the mentor group is even a number
const mentor_group_id = Number(query.mentor_group_id);
if (isNaN(mentor_group_id)) {
Expand Down Expand Up @@ -274,9 +265,6 @@ async function getMembers(request: Request, response: Response, next: NextFuncti
async function getAllCourseManager(_request: Request, response: Response, next: NextFunction) {
try {
const user: User = response.locals.user;
if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor");
}

const userInMentorGroup: UserBelongToMentorGroups[] = await UserBelongToMentorGroups.findAll({
where: {
Expand Down Expand Up @@ -389,13 +377,8 @@ async function removeMember(request: Request, response: Response, next: NextFunc
*/
async function getEndorsementGroupsByID(request: Request, response: Response, next: NextFunction) {
try {
const user: User = response.locals.user;
const params = request.params as { mentor_group_id: string };

if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor.");
}

Validator.validate(params, {
mentor_group_id: [ValidationTypeEnum.NON_NULL],
});
Expand Down
19 changes: 16 additions & 3 deletions backend/src/controllers/user/UserCourseController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Validator, { ValidationTypeEnum } from "../../utility/Validator";
import { MentorGroup } from "../../models/MentorGroup";
import { TrainingType } from "../../models/TrainingType";
import { HttpStatusCode } from "axios";
import { ForbiddenException } from "../../exceptions/ForbiddenException";
import PermissionHelper from "../../utility/helper/PermissionHelper";

/**
* Returns courses that are available to the current user (i.e. not enrolled in course)
Expand Down Expand Up @@ -177,13 +179,16 @@ async function withdrawFromCourseByUUID(request: Request, response: Response) {
/**
* Gets all courses that the current user can mentor (i.e. is member of a mentor group, which is
* assigned to a course)
* @param request
* @param _request
* @param response
* @param next
*/
async function getMentorable(request: Request, response: Response, next: NextFunction) {
async function getMentorable(_request: Request, response: Response, next: NextFunction) {
try {
const user: User = response.locals.user;
if (!(await user.isMentor())) {
throw new ForbiddenException("You are not a mentor");
}

const userWithCourses = await User.findOne({
where: {
Expand Down Expand Up @@ -240,10 +245,18 @@ async function getMentorable(request: Request, response: Response, next: NextFun
* Gets the courses that the user can actively edit
* These are all courses associated to a user through their respective
* mentor groups, where admin == true!
*
* This is only used to display the course list, so we can simply use the respective permission
* @param _request
* @param response
* @param next
*/
async function getEditable(request: Request, response: Response, next: NextFunction) {
async function getEditable(_request: Request, response: Response, next: NextFunction) {
try {
const user: User = response.locals.user;
if (!user.isMentor()) {
throw new ForbiddenException("You are not a mentor");
}

const dbUser = await User.findOne({
where: {
Expand Down
10 changes: 4 additions & 6 deletions backend/src/libraries/vateud/VateudCoreLibrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
VateudCoreSoloCreateT,
VateudCoreSoloRemoveResponseT,
VateudCoreSoloRemoveT,
VateudCoreTypeEnum
VateudCoreTypeEnum,
} from "./VateudCoreLibraryTypes";
import { Config } from "../../core/Config";
import { UserSolo } from "../../models/UserSolo";
Expand Down Expand Up @@ -42,7 +42,7 @@ async function _send<T>(props: SendT): Promise<T | undefined> {

return res.data as T;
} catch (e: any) {
console.error(e)
console.error(e);
Logger.log(LogLevels.LOG_WARN, e);
return undefined;
}
Expand Down Expand Up @@ -136,13 +136,13 @@ export async function removeSolo(userSolo: UserSolo) {
* - If it fails more than n times, then it really isn't our problem anymore tbh...
*/
export async function createEndorsement(userEndorsement: EndorsementGroupsBelongsToUsers, endorsementGroup: EndorsementGroup | null) {
if(!endorsementGroup) return false;
if (!endorsementGroup) return false;
const endorsementInfo: VateudCoreEndorsementCreateT = {
local_id: userEndorsement.id,
post_data: {
user_cid: userEndorsement.user_id,
position: endorsementGroup.name,
instructor_cid: 1439797,//todo userEndorsement.created_by,
instructor_cid: 1439797, //todo userEndorsement.created_by,
},
};

Expand All @@ -169,5 +169,3 @@ export async function createEndorsement(userEndorsement: EndorsementGroupsBelong

return true;
}


3 changes: 1 addition & 2 deletions backend/src/libraries/vateud/VateudCoreLibraryTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export type VateudCoreSoloCreateT = {
};
};


export type VateudCoreSoloCreateResponseT = {
success: boolean;
data: {
Expand Down Expand Up @@ -72,4 +71,4 @@ export type VateudCoreEndorsementCreateResponseT = {
created_at: string;
updated_at: string;
};
};
};
10 changes: 9 additions & 1 deletion backend/src/middlewares/ExceptionInterceptorMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ import { User } from "../models/User";

const sequelizeErrors = ["SequelizeValidationError", "SequelizeForeignKeyConstraintError", "SequelizeUniqueConstraintError"];

export async function exceptionInterceptorMiddleware(error: any, request: Request, response: Response, next: NextFunction) {
/**
* Intercepts and properly formats any exceptions that occur, such that the frontend
* can handle the data accordingly.
* @param error
* @param request
* @param response
* @param _next
*/
export async function exceptionInterceptorMiddleware(error: any, request: Request, response: Response, _next: NextFunction) {
console.error(error);

if (error instanceof UnauthorizedException) {
Expand Down
15 changes: 15 additions & 0 deletions backend/src/middlewares/IsMentorMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NextFunction, Request, Response } from "express";
import { User } from "../models/User";
import { ForbiddenException } from "../exceptions/ForbiddenException";

export async function isMentorMiddleware(_request: Request, response: Response, next: NextFunction) {
const user: User = response.locals.user;

if (!(await user.isMentor())) {
const exception = new ForbiddenException("You are not a mentor!");
next(exception);
return;
}

next();
}
19 changes: 14 additions & 5 deletions frontend/src/components/template/SideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,19 +175,28 @@ export function SideNav() {
elementTrue={
<>
<div className="menu-title menu-title-transparent">LM</div>
<MenuItem href={"administration/endorsement-group"} icon={<TbCertificate size={20} />}>
<MenuItem
requiredPerm={"lm.endorsement_groups.view"}
href={"administration/endorsement-group"}
icon={<TbCertificate size={20} />}>
Freigabegruppen
</MenuItem>
<MenuItem href={"administration/mentor-group"} icon={<TbUsers size={20} />}>
<MenuItem requiredPerm={"lm.mentor_group.view"} href={"administration/mentor-group"} icon={<TbUsers size={20} />}>
Mentorgruppen
</MenuItem>
<MenuItem href={"administration/course"} icon={<TbClipboardList size={20} />}>
<MenuItem requiredPerm={"lm.course.view"} href={"administration/course"} icon={<TbClipboardList size={20} />}>
Kurse
</MenuItem>
<MenuItem href={"administration/training-type"} icon={<TbTemplate size={20} />}>
<MenuItem
requiredPerm={"lm.training_types.view"}
href={"administration/training-type"}
icon={<TbTemplate size={20} />}>
Trainingstypen
</MenuItem>
<MenuItem icon={<TbAdjustments size={20} />} href={"administration/action-requirement"}>
<MenuItem
requiredPerm={"lm.action_requirements.view"}
icon={<TbAdjustments size={20} />}
href={"administration/action-requirement"}>
Aktionen | Bedingungen
</MenuItem>
</>
Expand Down

0 comments on commit cc35d78

Please sign in to comment.