Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#116 [FIX]: Added admin bypass for all authorization checks #129

Merged
merged 4 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/controllers/addressController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ import errorFormatter from '../services/errorFormatter';
import { count } from 'console';

const checkAuthorization = async (user: User, addressId: number | undefined, action: string) => {
if (user.role === UserRole.ADMIN) return;

switch (action) {
case 'create':
case 'update':
case 'delete':
// Only ADMINs can perform create/update/delete operations on addresses
if (user.role !== UserRole.ADMIN) {
throw new Error('This user is not authorized to perform this action');
}
throw new Error('This user is not authorized to perform this action');
break;
case 'getAll':
case 'get':
Expand Down
48 changes: 21 additions & 27 deletions src/controllers/applicationAnswerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,38 @@ const checkAuthorization = async (
applicationId: number | undefined,
action: string
) => {
if (user.role === UserRole.ADMIN) return;

switch (action) {
case 'create':
// Only ADMINs, the applier or viewers of the application can perform create operations on application answers
if (user.role !== UserRole.ADMIN) {
const application = await prismaClient.application.findUnique({
where: {
id: applicationId,
OR: [
{ visibility: VisibilityMode.PUBLIC },
{ viewersClassroom: { some: { users: { some: { id: user.id } } } } },
{ viewersUser: { some: { id: user.id } } },
{ applierId: user.id },
],
},
});
if (!application) {
throw new Error('This user is not authorized to perform this action.');
}
}
const application = await prismaClient.application.findUnique({
where: {
id: applicationId,
OR: [
{ visibility: VisibilityMode.PUBLIC },
{ viewersClassroom: { some: { users: { some: { id: user.id } } } } },
{ viewersUser: { some: { id: user.id } } },
{ applierId: user.id },
],
},
});
if (!application) throw new Error('This user is not authorized to perform this action.');

break;
case 'update':
case 'get':
case 'delete':
// Only ADMINs or the creator of the application answer can perform update/get/delete operations on application answers
if (user.role !== UserRole.ADMIN) {
const applicationAnswer = await prismaClient.applicationAnswer.findUnique({
where: { id: applicationAnswerId, userId: user.id },
});
if (!applicationAnswer) {
throw new Error('This user is not authorized to perform this action.');
}
}
const applicationAnswer = await prismaClient.applicationAnswer.findUnique({
where: { id: applicationAnswerId, userId: user.id },
});
if (!applicationAnswer) throw new Error('This user is not authorized to perform this action.');

break;
case 'getAll':
// Only ADMINs can perform get all application answers operation
if (user.role !== UserRole.ADMIN) {
throw new Error('This user is not authorized to perform this action.');
}
throw new Error('This user is not authorized to perform this action.');
break;
case 'getMy':
// All users can perform getMy operations on application answers (the results will be filtered based on the user)
Expand Down
88 changes: 34 additions & 54 deletions src/controllers/applicationController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,79 +15,59 @@ import prismaClient from '../services/prismaClient';
import errorFormatter from '../services/errorFormatter';

const checkAuthorization = async (user: User, applicationId: number | undefined, protocolId: number | undefined, action: string) => {
if (user.role === UserRole.ADMIN) return;

switch (action) {
case 'create':
// All users except USER can perform create operations on applications, but only if the protocol is public or the user is an applier
if (user.role !== UserRole.ADMIN) {
const protocol = await prismaClient.protocol.findUnique({
where: {
id: protocolId,
OR: [{ applicability: VisibilityMode.PUBLIC }, { appliers: { some: { id: user.id } } }],
enabled: true,
},
});

if (!protocol || user.role === UserRole.USER) {
throw new Error('This user is not authorized to perform this action');
}
}
const protocol = await prismaClient.protocol.findUnique({
where: {
id: protocolId,
OR: [{ applicability: VisibilityMode.PUBLIC }, { appliers: { some: { id: user.id } } }],
enabled: true,
},
});
if (!protocol || user.role === UserRole.USER) throw new Error('This user is not authorized to perform this action');
break;
case 'update':
// Only ADMINs or the applier can perform update operations on applications
if (user.role !== UserRole.ADMIN) {
const application = await prismaClient.application.findUnique({
where: {
id: applicationId,
applierId: user.id,
},
});
if (!application) {
throw new Error('This user is not authorized to perform this action');
}
}
const updateApplication = await prismaClient.application.findUnique({
where: {
id: applicationId,
applierId: user.id,
},
});
if (!updateApplication) throw new Error('This user is not authorized to perform this action');
break;
case 'getMy':
case 'getVisible':
// All users can perform getMy/getVisible operations on applications (the result will be filtered based on the user)
break;
case 'getAll':
// Only ADMINs can perform getAll operations on applications
if (user.role !== UserRole.ADMIN) {
throw new Error('This user is not authorized to perform this action');
}
throw new Error('This user is not authorized to perform this action');
break;
case 'get':
// Only ADMINs or the viewers of the application can perform get operations on applications
if (user.role !== UserRole.ADMIN) {
const application = await prismaClient.application.findUnique({
where: {
id: applicationId,
OR: [
{ visibility: 'PUBLIC' },
{ viewersClassroom: { some: { users: { some: { id: user.id } } } } },
{ viewersUser: { some: { id: user.id } } },
{ applierId: user.id },
],
},
});
if (!application) {
throw new Error('This user is not authorized to perform this action');
}
}
const getApplication = await prismaClient.application.findUnique({
where: {
id: applicationId,
OR: [
{ visibility: 'PUBLIC' },
{ viewersClassroom: { some: { users: { some: { id: user.id } } } } },
{ viewersUser: { some: { id: user.id } } },
{ applierId: user.id },
],
},
});
if (!getApplication) throw new Error('This user is not authorized to perform this action');
break;
case 'delete':
// Only ADMINs or the applier can perform delete operations on applications
if (user.role !== UserRole.ADMIN) {
const application = await prismaClient.application.findUnique({
where: {
id: applicationId,
applierId: user.id,
},
});
if (!application) {
throw new Error('This user is not authorized to perform this action');
}
}
const deleteApplication = await prismaClient.application.findUnique({
where: { id: applicationId, applierId: user.id },
});
if (!deleteApplication) throw new Error('This user is not authorized to perform this action');
break;
}
};
Expand Down
103 changes: 51 additions & 52 deletions src/controllers/authController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,57 @@ import errorFormatter from '../services/errorFormatter';
import ms from 'ms';
import { compareSync, hashSync } from 'bcrypt';

export const signUp = async (req: Request, res: Response) => {
try {
// Yup schemas
const signUpSchema = yup
.object()
.shape({
id: yup.number(),
name: yup.string().min(1).max(255).required(),
username: yup.string().min(3).max(20).required(),
hash: yup.string().required(),
role: yup.string().oneOf(Object.values(UserRole)).required(),
institutionId: yup.number(),
classrooms: yup.array().of(yup.number()).default([]),
})
.noUnknown();
// Yup parsing/validation
const signingUser = await signUpSchema.validate(req.body, { stripUnknown: false });
// Password encryption
signingUser.hash = hashSync(signingUser.hash, 10);
// Prisma operation
const createdUser = await prismaClient.user.create({
data: {
id: signingUser.id,
name: signingUser.name,
username: signingUser.username,
hash: signingUser.hash,
role: signingUser.role,
institutionId: signingUser.institutionId,
classrooms: { connect: signingUser.classrooms.map((classroomId) => ({ id: classroomId })) },
},
include: { profileImage: true },
});
// JWT token creation
const token = jwt.sign({ id: createdUser.id, username: createdUser.username }, process.env.JWT_SECRET as string, {
expiresIn: process.env.JWT_EXPIRATION,
});

res.status(201).json({
message: 'User signed up.',
data: {
id: createdUser.id,
role: createdUser.role,
token: token,
expiresIn: process.env.JWT_EXPIRATION,
institutionId: createdUser.institutionId,
profileImage: createdUser.profileImage ? { path: createdUser.profileImage.path } : undefined,
},
});
} catch (error: any) {
res.status(400).json(errorFormatter(error));
}
};
// export const signUp = async (req: Request, res: Response) => {
// try {
// // Yup schemas
// const signUpSchema = yup
// .object()
// .shape({
// id: yup.number(),
// name: yup.string().min(1).max(255).required(),
// username: yup.string().min(3).max(20).required(),
// hash: yup.string().required(),
// role: yup.string().oneOf(Object.values(UserRole)).required(),
// institutionId: yup.number(),
// classrooms: yup.array().of(yup.number()).default([]),
// })
// .noUnknown();
// // Yup parsing/validation
// const signingUser = await signUpSchema.validate(req.body, { stripUnknown: false });
// // Password encryption
// signingUser.hash = hashSync(signingUser.hash, 10);
// // Prisma operation
// const createdUser = await prismaClient.user.create({
// data: {
// id: signingUser.id,
// name: signingUser.name,
// username: signingUser.username,
// hash: signingUser.hash,
// role: signingUser.role,
// institutionId: signingUser.institutionId,
// classrooms: { connect: signingUser.classrooms.map((classroomId) => ({ id: classroomId })) },
// },
// include: { profileImage: true },
// });
// // JWT token creation
// const token = jwt.sign({ id: createdUser.id, username: createdUser.username }, process.env.JWT_SECRET as string, {
// expiresIn: process.env.JWT_EXPIRATION,
// });
// res.status(201).json({
// message: 'User signed up.',
// data: {
// id: createdUser.id,
// role: createdUser.role,
// token: token,
// expiresIn: process.env.JWT_EXPIRATION,
// institutionId: createdUser.institutionId,
// profileImage: createdUser.profileImage ? { path: createdUser.profileImage.path } : undefined,
// },
// });
// } catch (error: any) {
// res.status(400).json(errorFormatter(error));
// }
// };

export const signIn = async (req: Request, res: Response) => {
try {
Expand Down
50 changes: 21 additions & 29 deletions src/controllers/classroomController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,50 +32,42 @@ const publicFields = {

// Only admins or the coordinator of the institution can perform C-UD operations on classrooms
const checkAuthorization = async (user: User, classroomId: number | undefined, institutionId: number | undefined, action: string) => {
if (user.role === UserRole.ADMIN) return;

switch (action) {
case 'create':
// Only ADMINs or members of an institution can perform create operations on its classrooms
if (user.role !== UserRole.ADMIN && (user.role === UserRole.USER || (institutionId && user.institutionId !== institutionId))) {
if (user.role === UserRole.USER || (institutionId && user.institutionId !== institutionId))
throw new Error('This user is not authorized to perform this action');
}
break;
case 'update':
case 'delete':
// Only ADMINs, COORDINATORs and PUBLISHERs of an institution can perform update/delete operations on its classrooms
if (user.role !== UserRole.ADMIN) {
const classroom = await prismaClient.classroom.findUnique({
where: user.institutionId ? { id: classroomId, institutionId: user.institutionId } : { id: classroomId },
});
if (
user.role === UserRole.USER ||
user.role === UserRole.APPLIER ||
(institutionId && institutionId !== user.institutionId) ||
!classroom
) {
throw new Error('This user is not authorized to perform this action');
}
}
const deleteClassroom = await prismaClient.classroom.findUnique({
where: user.institutionId ? { id: classroomId, institutionId: user.institutionId } : { id: classroomId },
});
if (
user.role === UserRole.USER ||
user.role === UserRole.APPLIER ||
(institutionId && institutionId !== user.institutionId) ||
!deleteClassroom
)
throw new Error('This user is not authorized to perform this action');

break;
case 'getAll':
// Only ADMINs can perform get all classrooms operation
if (user.role !== UserRole.ADMIN) {
throw new Error('This user is not authorized to perform this action');
}
throw new Error('This user is not authorized to perform this action');
break;
case 'get': // Only ADMINs or members (except USERs) of an institution can perform get operations on its classrooms
if (user.role !== UserRole.ADMIN) {
const classroom = await prismaClient.classroom.findUnique({
where: user.institutionId ? { id: classroomId, institutionId: user.institutionId } : { id: classroomId },
});
if (user.role === UserRole.USER || !user.institutionId || !classroom) {
throw new Error('This user is not authorized to perform this action');
}
}
const getClassroom = await prismaClient.classroom.findUnique({
where: user.institutionId ? { id: classroomId, institutionId: user.institutionId } : { id: classroomId },
});
if (user.role === UserRole.USER || !user.institutionId || !getClassroom)
throw new Error('This user is not authorized to perform this action');
break;
case 'search':
if (user.role === UserRole.USER) {
throw new Error('This user is not authorized to perform this action');
}
if (user.role === UserRole.USER) throw new Error('This user is not authorized to perform this action');
break;
case 'getMy':
// All users can perform get my classrooms operation (the result will be filtered based on the user)
Expand Down
Loading