From 60100273fbef2bb2696629c536b7f997f14c02bd Mon Sep 17 00:00:00 2001 From: Laurin <60652077+Flexla54@users.noreply.github.com> Date: Mon, 6 Jun 2022 18:34:59 +0200 Subject: [PATCH] added STANDARD admin responsibility --- src/Controllers/group.controllers.ts | 4 +-- src/Controllers/participant.controller.ts | 35 ++++++++++++---------- src/Controllers/role.controller.ts | 16 ++++++---- src/Controllers/team.controller.ts | 36 +++++++++++++++++++++-- src/Controllers/user_auth.controller.ts | 7 ++--- src/Middleware/auth/auth.ts | 16 ++++++++-- src/Middleware/auth/teamleaderAuth.ts | 3 +- src/Routes/participant.routes.ts | 5 ++-- src/Routes/role.routes.ts | 7 +++++ src/Routes/team.routes.ts | 6 ---- 10 files changed, 92 insertions(+), 43 deletions(-) diff --git a/src/Controllers/group.controllers.ts b/src/Controllers/group.controllers.ts index 2a3c6c3..102ffd5 100644 --- a/src/Controllers/group.controllers.ts +++ b/src/Controllers/group.controllers.ts @@ -3,7 +3,7 @@ import { PrismaClientKnownRequestError, PrismaClientUnknownRequestError } from " import { Request, Response } from "express"; import { z } from "zod"; import prisma from "../lib/prisma"; -import { requireResponsibleForGroup } from "../Middleware/auth/auth"; +import { requireResponsibleForGroups } from "../Middleware/auth/auth"; import NotFoundError from "../Middleware/error/NotFoundError"; import { createInsufficientPermissionsError, @@ -163,7 +163,7 @@ export const updateGroup = async (req: Request<{ pid: string }>, res: Response) const body = result.data; const { pid } = req.params; - requireResponsibleForGroup(req.auth, pid); + requireResponsibleForGroups(req.auth, pid); try { const group = await prisma.group.update({ diff --git a/src/Controllers/participant.controller.ts b/src/Controllers/participant.controller.ts index ee53b5e..2c6d242 100644 --- a/src/Controllers/participant.controller.ts +++ b/src/Controllers/participant.controller.ts @@ -5,7 +5,8 @@ import { DataType, generateError, generateInvalidBodyError } from "./common"; import { Prisma } from "@prisma/client"; import { PrismaClientKnownRequestError } from "@prisma/client/runtime"; import NotFoundError from "../Middleware/error/NotFoundError"; -import { requireLeaderOfTeam } from "../Middleware/auth/teamleaderAuth"; +import { requireLeaderOfTeam, requireResponsibleForParticipant } from "../Middleware/auth/teamleaderAuth"; +import { requireResponsibleForGroups } from "../Middleware/auth/auth"; const InitialParticipant = z.object({ firstName: z.string(), @@ -35,7 +36,9 @@ const returnedParticipant = { } as const; // at: POST api/participants/ -export const createParticipant = async (req: Request, res: Response) => { +export const createParticipant = async (req: Request<{ teamPid: string }>, res: Response) => { + const { teamPid } = req.params; + const result = ParticipantBody.safeParse(req.body); if (result.success === false) { @@ -45,7 +48,6 @@ export const createParticipant = async (req: Request, res: Response) => { firstname: DataType.STRING, lastName: DataType.STRING, groupPid: DataType.UUID, - teamPid: DataType.UUID, }, result.error ) @@ -54,7 +56,9 @@ export const createParticipant = async (req: Request, res: Response) => { const body = result.data; if (req.teamleader?.isAuthenticated) { - requireLeaderOfTeam(req.teamleader, body.teamPid); + await requireLeaderOfTeam(req.teamleader, teamPid); + } else { + requireResponsibleForGroups(req.auth, body.groupPid); } try { @@ -98,10 +102,9 @@ export const createParticipant = async (req: Request, res: Response) => { // at: PATCH api/participants/:pid/ export const updateParticipant = async (req: Request<{ pid: string }>, res: Response) => { const { pid } = req.params; - const teamPid = await getTeamPidByParticipantPid(pid); if (req.teamleader?.isAuthenticated) { - requireLeaderOfTeam(req.teamleader, teamPid); + requireResponsibleForParticipant(req.teamleader, pid); } const result = InitialParticipant.partial().safeParse(req.body); @@ -127,7 +130,7 @@ export const updateParticipant = async (req: Request<{ pid: string }>, res: Resp data: { firstName: body.firstName, lastName: body.lastName, - group: { connect: { pid: body.groupPid } }, + ...(body.groupPid ? { group: { connect: { pid: body.groupPid } } } : {}), }, select: returnedParticipant, }); @@ -148,10 +151,11 @@ export const updateParticipant = async (req: Request<{ pid: string }>, res: Resp // at: DELETE api/participants/:pid/ export const deleteParticipant = async (req: Request<{ pid: string }>, res: Response) => { const { pid } = req.params; - const teamPid = await getTeamPidByParticipantPid(pid); if (req.teamleader?.isAuthenticated) { - requireLeaderOfTeam(req.teamleader, teamPid); + requireResponsibleForParticipant(req.teamleader, pid); + } else { + await requireResponsibleForGroups(req.auth, await getGroupByParticipantPid(pid)); } try { @@ -167,15 +171,14 @@ export const deleteParticipant = async (req: Request<{ pid: string }>, res: Resp } }; -export const getTeamPidByParticipantPid = async function (partPid: string) { - const participant = await prisma.participant.findUnique({ - where: { pid: partPid }, - select: { team: { select: { pid: true } } } - }); +export async function getGroupByParticipantPid(partPid: string) { + const parti = ( + await prisma.participant.findUnique({ where: { pid: partPid }, select: { group: true } }) + )?.group.pid; - if (!participant) { + if (!parti) { throw new NotFoundError("participant", partPid); } - return participant.team.pid; + return parti; } diff --git a/src/Controllers/role.controller.ts b/src/Controllers/role.controller.ts index 250fa97..c9e55bf 100644 --- a/src/Controllers/role.controller.ts +++ b/src/Controllers/role.controller.ts @@ -1,11 +1,12 @@ -import { Prisma, Role } from "@prisma/client"; import { Request, Response } from "express"; import { z } from "zod"; import prisma from "../lib/prisma"; +import { requireResponsibleForGroups } from "../Middleware/auth/auth"; import { requireLeaderOfTeam, requireResponsibleForParticipant } from "../Middleware/auth/teamleaderAuth"; import NotFoundError from "../Middleware/error/NotFoundError"; -import { createInsufficientPermissionsError, DataType, generateInvalidBodyError } from "./common"; -import { getTeamPidByParticipantPid } from "./participant.controller"; +import { DataType, generateInvalidBodyError } from "./common"; +import { getGroupByParticipantPid } from "./participant.controller"; +import { getGroupsByTeamPid } from "./team.controller"; require("express-async-errors"); @@ -58,7 +59,9 @@ export async function getRolesForTeam(req: Request<{ pid: string }>, res: Respon const pid = req.params.pid; if (req.teamleader?.isAuthenticated) { - requireLeaderOfTeam(req.teamleader, pid); + await requireLeaderOfTeam(req.teamleader, pid); + } else { + await requireResponsibleForGroups(req.auth, await getGroupsByTeamPid(pid)); } const roles = await prisma.role.findMany({ @@ -95,10 +98,11 @@ export async function assignParticipantToRole(req: Request<{ pid: string }>, res } const { participantPid } = zBody.data; - const teamPid = await getTeamPidByParticipantPid(pid); if (req.teamleader?.isAuthenticated) { - requireLeaderOfTeam(req.teamleader, teamPid); + requireResponsibleForParticipant(req.teamleader, participantPid); + } else { + await requireResponsibleForGroups(req.auth, await getGroupByParticipantPid(participantPid)); } const schema = await prisma.role.findFirst({ diff --git a/src/Controllers/team.controller.ts b/src/Controllers/team.controller.ts index 47945e3..3db62bd 100644 --- a/src/Controllers/team.controller.ts +++ b/src/Controllers/team.controller.ts @@ -5,6 +5,7 @@ import { requireLeaderOfTeam } from "../Middleware/auth/teamleaderAuth"; import { TeamBody } from "./user_auth.controller"; import { Prisma } from "@prisma/client"; import NotFoundError from "../Middleware/error/NotFoundError"; +import { requireResponsibleForGroups } from "../Middleware/auth/auth"; export const basicTeam = { pid: true, @@ -36,7 +37,9 @@ export const getTeam = async (req: Request<{ pid: string }>, res: Response) => { const { pid } = req.params; if (req.teamleader?.isAuthenticated) { - requireLeaderOfTeam(req.teamleader, pid); + await requireLeaderOfTeam(req.teamleader, pid); + } else { + await requireResponsibleForGroups(req.auth, pid); } const team = await prisma.team.findUnique({ @@ -55,7 +58,9 @@ export const updateTeam = async (req: Request, res: Response) => { const { pid } = req.params; if (req.teamleader?.isAuthenticated) { - requireLeaderOfTeam(req.teamleader, pid); + await requireLeaderOfTeam(req.teamleader, pid); + } else { + await requireResponsibleForGroups(req.auth, await getGroupsByTeamPid(pid)); } const result = TeamBody.omit({ partGroupId: true, partFirstName: true, partLastName: true }).safeParse(req.body); @@ -102,10 +107,35 @@ export const deleteTeam = async (req: Request, res: Response) => { const { pid } = req.params; if (req.teamleader?.isAuthenticated) { - requireLeaderOfTeam(req.teamleader, pid); + await requireLeaderOfTeam(req.teamleader, pid); + } else { + await requireResponsibleForGroups(req.auth, await getGroupsByTeamPid(pid)); } await prisma.team.delete({ where: { pid } }); res.status(204).json({ type: "success", payload: { message: "Sucesfully deleted team" } }); }; + +export async function checkTeamExistence(teamPid: string) { + const teamCount = await prisma.team.count({ + where: { pid: teamPid, } + }); + if (teamCount == 0) { + throw new NotFoundError("team", teamPid); + } +} + +export async function getGroupsByTeamPid(teamPid: string) { + const team = ( + await prisma.team.findUnique({ where: { pid: teamPid }, select: { participants: { select: { group: true } } } }) + ); + + let groups: string[] = []; + + team?.participants.forEach(participant => { + groups.push(participant.group.pid); + }); + + return groups; +} diff --git a/src/Controllers/user_auth.controller.ts b/src/Controllers/user_auth.controller.ts index 89824e4..369f596 100644 --- a/src/Controllers/user_auth.controller.ts +++ b/src/Controllers/user_auth.controller.ts @@ -3,11 +3,10 @@ import prisma from "../lib/prisma"; import { mailClient } from "../lib/redis"; import { nanoid } from "nanoid"; import { verificationMail } from "../lib/mail"; -import { createInsufficientPermissionsError, DataType, generateError, generateInvalidBodyError } from "./common"; -import { generateTeamleaderJWT, requireLeaderOfTeam } from "../Middleware/auth/teamleaderAuth"; +import { DataType, generateError, generateInvalidBodyError } from "./common"; +import { generateTeamleaderJWT } from "../Middleware/auth/teamleaderAuth"; import { createRolesForTeam } from "./role.controller"; -import { any, z } from "zod"; -import { basicTeam } from "./team.controller"; +import { z } from "zod"; export const TeamBody = z.object({ teamName: z.string().min(1), diff --git a/src/Middleware/auth/auth.ts b/src/Middleware/auth/auth.ts index 949f8bc..411b515 100644 --- a/src/Middleware/auth/auth.ts +++ b/src/Middleware/auth/auth.ts @@ -173,12 +173,22 @@ export const requireConfiguredAuthentication = next(); }; -export function requireResponsibleForGroup(auth: AuthJWTPayload | undefined, groupPid: string) { +export function requireResponsibleForGroups(auth: AuthJWTPayload | undefined, groupPids: string[] | string) { if (auth?.permission_level === "ELEVATED") { return; } - if (!auth?.groups.includes(groupPid)) { - throw new AuthError("The provided authorization is not valid for the requested operation!"); + if (Array.isArray(groupPids)) { + groupPids.forEach(gr => { + if (auth?.groups.includes(gr)) { + return; + } + + throw new AuthError("The provided authorization is not valid for the requested operation!"); + }); + } else { + if (auth?.groups.includes(groupPids)) { + throw new AuthError("The provided authorization is not valid for the requested operation!"); + } } } diff --git a/src/Middleware/auth/teamleaderAuth.ts b/src/Middleware/auth/teamleaderAuth.ts index 61fc82e..f0a4737 100644 --- a/src/Middleware/auth/teamleaderAuth.ts +++ b/src/Middleware/auth/teamleaderAuth.ts @@ -85,7 +85,8 @@ export const _requireTeamleaderAuthentication = export const requireTeamleaderAuthentication = _requireTeamleaderAuthentication({ optional: false, controlled: false }); -export function requireLeaderOfTeam(auth: TeamleaderJWTPayload | undefined, teamPid: string) { +export async function requireLeaderOfTeam(auth: TeamleaderJWTPayload | undefined, teamPid: string) { + await checkTeamExistence(teamPid); if (auth?.team !== teamPid) { throw new AuthError("The provided authorization is not valid for the requested team"); } diff --git a/src/Routes/participant.routes.ts b/src/Routes/participant.routes.ts index d35fae6..2e76a2e 100644 --- a/src/Routes/participant.routes.ts +++ b/src/Routes/participant.routes.ts @@ -1,11 +1,12 @@ import express from "express"; import { createParticipant, deleteParticipant, updateParticipant } from "../Controllers/participant.controller"; import { requireConfiguredAuthentication } from "../Middleware/auth/auth"; +import teamRouter from "./team.routes"; const router = express.Router(); -router.post( - "/", +teamRouter.post<"/:teamPid/participants", { teamPid: string }>( + "/:teamPid/participants", requireConfiguredAuthentication({ optional: false, type: { admin: true, teamleader: true } }), createParticipant ); diff --git a/src/Routes/role.routes.ts b/src/Routes/role.routes.ts index b23d118..81ee739 100644 --- a/src/Routes/role.routes.ts +++ b/src/Routes/role.routes.ts @@ -1,6 +1,7 @@ import Express from "express"; import { assignParticipantToRole, getRolesForTeam } from "../Controllers/role.controller"; import { requireConfiguredAuthentication } from "../Middleware/auth/auth"; +import teamRouter from "./team.routes"; const router = Express.Router(); @@ -10,4 +11,10 @@ router.put<"/:pid/participant", { pid: string }>( assignParticipantToRole ); +teamRouter.get<"/:pid/roles", { pid: string }>( + "/:pid/roles", + requireConfiguredAuthentication({ optional: false, type: { admin: true, teamleader: true } }), + getRolesForTeam +); + export default router; \ No newline at end of file diff --git a/src/Routes/team.routes.ts b/src/Routes/team.routes.ts index a49789a..7a79ea1 100644 --- a/src/Routes/team.routes.ts +++ b/src/Routes/team.routes.ts @@ -23,10 +23,4 @@ router.delete<"/:pid/", { pid: string }>( deleteTeam ); -router.get<"/:pid/roles", { pid: string }>( - "/:pid/roles", - requireConfiguredAuthentication({ optional: false, type: { admin: true, teamleader: true } }), - getRolesForTeam -); - export default router;