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

Permissions revamp #471

Draft
wants to merge 49 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7964d7d
add loginCheckedPut
henry-li-06 May 13, 2023
f9431b3
update member endpoint paths
henry-li-06 May 13, 2023
b14dae8
update team endpoints
henry-li-06 May 14, 2023
a0ce03e
update member image endpoints
henry-li-06 May 14, 2023
65b144f
change frontend endpoints
henry-li-06 May 14, 2023
6b445d1
add missing func param
henry-li-06 May 14, 2023
ce8cc81
same issue
henry-li-06 May 14, 2023
9e004be
update shoutouts endpoints
henry-li-06 May 14, 2023
3166e0f
remove /isAdmin endpoint
henry-li-06 May 14, 2023
2dc4c4d
update team event endpoints
henry-li-06 May 15, 2023
528b40f
update dev portfolio endpoints
henry-li-06 May 15, 2023
4ae2224
update candidate decider endpoints
henry-li-06 May 15, 2023
2e04bb8
fix devportfoliodetails
henry-li-06 May 15, 2023
7bba70f
create auth.ts
henry-li-06 May 15, 2023
35616f1
create memberRouter
henry-li-06 May 15, 2023
71271d9
add more routers
henry-li-06 May 16, 2023
8b98db7
more stuff
henry-li-06 May 19, 2023
3e740b0
more stuff
henry-li-06 May 19, 2023
f3d95cb
more router stuff
henry-li-06 May 19, 2023
507eb0a
setup sub-routers for dev portfolio
henry-li-06 May 20, 2023
45d8a45
create auth role types
henry-li-06 May 20, 2023
4b7e5a6
setup firebase for auth roles collection
henry-li-06 May 20, 2023
5b89be8
add resource names to dev portfolio api login checked routes
henry-li-06 May 20, 2023
b4d3b14
add rbac auth middleware
henry-li-06 May 20, 2023
540239b
add auth role dao
henry-li-06 May 20, 2023
fda82ec
configure rbac for dev portfolio
henry-li-06 May 20, 2023
964eb6e
save authed user in res.locals
henry-li-06 May 21, 2023
c31d324
🤷🏻‍♂️
henry-li-06 May 22, 2023
a71f4a3
change meta and owner rbac config
henry-li-06 May 22, 2023
60e60b9
setup auth rules for profile images
henry-li-06 May 22, 2023
ed5935f
setup auth rules for members
henry-li-06 May 22, 2023
0dd80d4
setup rbac for shoutotus
henry-li-06 May 22, 2023
e9f6491
setup rbac for teams
henry-li-06 May 22, 2023
b8bbf7f
setup rbac for tec
henry-li-06 May 22, 2023
32ec982
create separate files for routers
henry-li-06 May 22, 2023
0a132ab
remove redundant permissions check in api logic
henry-li-06 May 22, 2023
cd22b75
remove todo
henry-li-06 May 22, 2023
bd63330
create rbacconfig type
henry-li-06 May 22, 2023
b5873b3
change logic for handling lead types
henry-li-06 May 22, 2023
44e2986
Fix firebase import
henry-li-06 May 22, 2023
676c8cc
configure teamRouter
henry-li-06 May 30, 2023
5977e72
make auth middleware more extensible
henry-li-06 Jan 26, 2024
e4b4d23
redo dev portfolio routes
henry-li-06 Jan 26, 2024
34d5d51
redo image routes
henry-li-06 Jan 26, 2024
672ca78
redo member router
henry-li-06 Jan 26, 2024
baf5276
redo shoutouts router
henry-li-06 Jan 26, 2024
45a408e
redo team event router
henry-li-06 Jan 26, 2024
ff58c2d
redo team router
henry-li-06 Jan 26, 2024
9cbe06e
too lazy to fix candidate decider
henry-li-06 Jan 26, 2024
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
40 changes: 40 additions & 0 deletions backend/rbac.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"resources": {
"dev-portfolio": {
"read": ["lead", "admin"],
"write": ["dev_lead", "lead", "admin"]
},
"dev-portfolio-submission": {
"read": ["lead", "dev_lead", "admin"],
"write": ["dev_lead", "admin"]
},
"profile-image": {
"read": ["lead", "admin"],
"write": ["lead", "admin"]
},
"member": {
"read": ["lead", "admin"],
"write": ["lead", "admin"]
},
"member-diff": {
"read": ["lead", "admin"],
"write": ["lead", "admin"]
},
"shoutout": {
"read": ["lead", "admin"],
"write": ["lead", "admin"]
},
"team": {
"read": ["lead", "admin"],
"write": ["lead", "admin"]
},
"team-event": {
"read": ["lead", "ci_lead", "admin"],
"write": ["ci_lead", "admin"]
},
"team-event-attendance": {
"read": ["lead", "ci_lead", "admin"],
"write": ["ci_lead", "admin"]
}
}
}
44 changes: 3 additions & 41 deletions backend/src/API/devPortfolioAPI.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import { DateTime } from 'luxon';
import DevPortfolioDao from '../dao/DevPortfolioDao';
import PermissionsManager from '../utils/permissionsManager';
import { PermissionError, BadRequestError, NotFoundError } from '../utils/errors';
import { BadRequestError, NotFoundError } from '../utils/errors';
import { validateSubmission, isWithinDates } from '../utils/githubUtil';

const zonedTime = (timestamp: number, ianatz = 'America/New_York') =>
DateTime.fromMillis(timestamp, { zone: ianatz });

export const devPortfolioDao = new DevPortfolioDao();

export const getAllDevPortfolios = async (user: IdolMember): Promise<DevPortfolio[]> => {
const isLeadOrAdmin = await PermissionsManager.isLeadOrAdmin(user);
if (!isLeadOrAdmin)
throw new PermissionError(
`User with email ${user.email} does not have permission to view dev portfolios!`
);
return devPortfolioDao.getAllInstances();
};
export const getAllDevPortfolios = async (user: IdolMember): Promise<DevPortfolio[]> =>
devPortfolioDao.getAllInstances();

export const getAllDevPortfolioInfo = async (): Promise<DevPortfolioInfo[]> =>
devPortfolioDao.getAllDevPortfolioInfo();
Expand All @@ -30,11 +23,6 @@ export const getUsersDevPortfolioSubmissions = async (
): Promise<DevPortfolioSubmission[]> => devPortfolioDao.getUsersDevPortfolioSubmissions(uuid, user);

export const getDevPortfolio = async (uuid: string, user: IdolMember): Promise<DevPortfolio> => {
const isLeadOrAdmin = await PermissionsManager.isLeadOrAdmin(user);
if (!isLeadOrAdmin)
throw new PermissionError(
`User with email ${user.email} does not have permission to view dev portfolios!`
);
const devPortfolio = await devPortfolioDao.getInstance(uuid);
if (!devPortfolio) throw new NotFoundError(`Dev portfolio with uuid: ${uuid} does not exist!`);
return devPortfolio;
Expand All @@ -44,12 +32,6 @@ export const createNewDevPortfolio = async (
instance: DevPortfolio,
user: IdolMember
): Promise<DevPortfolio> => {
const canCreateDevPortfolio = await PermissionsManager.isLeadOrAdmin(user);
if (!canCreateDevPortfolio) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to create dev portfolio!`
);
}
if (
!instance.name ||
instance.name.length === 0 ||
Expand Down Expand Up @@ -77,12 +59,6 @@ export const createNewDevPortfolio = async (
};

export const deleteDevPortfolio = async (uuid: string, user: IdolMember): Promise<void> => {
const canDeleteDevPortfolio = await PermissionsManager.isLeadOrAdmin(user);
if (!canDeleteDevPortfolio) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to delete dev portfolio!`
);
}
await devPortfolioDao.deleteInstance(uuid);
};

Expand Down Expand Up @@ -116,14 +92,6 @@ export const updateSubmissions = async (
updatedSubmissions: DevPortfolioSubmission[],
user: IdolMember
): Promise<DevPortfolio> => {
const canChangeSubmission = await PermissionsManager.isLeadOrAdmin(user);

if (!canChangeSubmission) {
throw new PermissionError(
`User with email ${user.email} does not have permission to update dev portfolio submissions`
);
}

const devPortfolio = await devPortfolioDao.getInstance(uuid);
if (!devPortfolio) {
throw new BadRequestError(`Dev portfolio with uuid: ${uuid} does not exist`);
Expand All @@ -138,12 +106,6 @@ export const updateSubmissions = async (
};

export const regradeSubmissions = async (uuid: string, user: IdolMember): Promise<DevPortfolio> => {
const canRequestRegrade = await PermissionsManager.isLeadOrAdmin(user);
if (!canRequestRegrade)
throw new PermissionError(
`User with email ${user.email} does not have permission to regrade dev portfolio submissions`
);

const devPortfolio = await devPortfolioDao.getInstance(uuid);
if (!devPortfolio) {
throw new BadRequestError(`Dev portfolio with uuid: ${uuid} does not exist`);
Expand Down
39 changes: 3 additions & 36 deletions backend/src/API/memberAPI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Request } from 'express';
import MembersDao from '../dao/MembersDao';
import PermissionsManager from '../utils/permissionsManager';
import { BadRequestError, PermissionError } from '../utils/errors';
import { bucket } from '../firebase';
import { getNetIDFromEmail, computeMembersDiff } from '../utils/memberUtil';
Expand All @@ -14,12 +13,6 @@ export const allApprovedMembers = (): Promise<readonly IdolMember[]> =>
MembersDao.getAllMembers(true);

export const setMember = async (body: IdolMember, user: IdolMember): Promise<IdolMember> => {
const canEdit = await PermissionsManager.canEditMembers(user);
if (!canEdit) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to edit members!`
);
}
if (!body.email || body.email === '') {
throw new BadRequestError("Couldn't edit member with undefined email!");
}
Expand All @@ -34,21 +27,13 @@ export const updateMember = async (
body: IdolMember,
user: IdolMember
): Promise<unknown> => {
const canEdit = await PermissionsManager.canEditMembers(user);
if (!canEdit && user.email !== body.email) {
// members are able to edit their own information
throw new PermissionError(
`User with email: ${user.email} does not have permission to edit members!`
);
}
if (!body.email || body.email === '') {
throw new BadRequestError("Couldn't edit member with undefined email!");
}
if (
!canEdit &&
(body.role !== user.role ||
body.firstName !== user.firstName ||
body.lastName !== user.lastName)
body.role !== user.role ||
body.firstName !== user.firstName ||
body.lastName !== user.lastName
) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to edit member name or roles!`
Expand All @@ -62,12 +47,6 @@ export const updateMember = async (
};

export const deleteMember = async (email: string, user: IdolMember): Promise<void> => {
const canEdit = await PermissionsManager.canEditMembers(user);
if (!canEdit) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to delete members!`
);
}
if (!email || email === '') {
throw new BadRequestError("Couldn't delete member with undefined email!");
}
Expand All @@ -90,12 +69,6 @@ export const deleteImage = async (email: string): Promise<void> => {
export const getUserInformationDifference = async (
user: IdolMember
): Promise<readonly IdolMemberDiff[]> => {
const canReview = await PermissionsManager.canReviewChanges(user);
if (!canReview) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to review members information diff!`
);
}
const [allApprovedMembersList, allLatestMembersList] = await Promise.all([
allApprovedMembers(),
allMembers()
Expand All @@ -108,12 +81,6 @@ export const reviewUserInformationChange = async (
rejected: readonly string[],
user: IdolMember
): Promise<void> => {
const canReview = await PermissionsManager.canReviewChanges(user);
if (!canReview) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to review members information diff!`
);
}
await Promise.all([
MembersDao.approveMemberInformationChanges(approved),
MembersDao.revertMemberInformationChanges(rejected)
Expand Down
23 changes: 1 addition & 22 deletions backend/src/API/shoutoutAPI.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PermissionsManager from '../utils/permissionsManager';
import { NotFoundError, PermissionError } from '../utils/errors';
import ShoutoutsDao from '../dao/ShoutoutsDao';

Expand All @@ -19,27 +18,13 @@ export const getShoutouts = async (
memberEmail: string,
type: 'given' | 'received',
user: IdolMember
): Promise<Shoutout[]> => {
const canEdit: boolean = await PermissionsManager.canGetShoutouts(user);
if (!canEdit && memberEmail !== user.email) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to get shoutouts!`
);
}
return shoutoutsDao.getShoutouts(memberEmail, type);
};
): Promise<Shoutout[]> => shoutoutsDao.getShoutouts(memberEmail, type);

export const hideShoutout = async (
uuid: string,
hide: boolean,
user: IdolMember
): Promise<void> => {
const canEdit = await PermissionsManager.canHideShoutouts(user);
if (!canEdit) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to hide shoutouts!`
);
}
const shoutout = await shoutoutsDao.getShoutout(uuid);
if (!shoutout) throw new NotFoundError(`Shoutout with uuid: ${uuid} does not exist!`);
await shoutoutsDao.updateShoutout({ ...shoutout, hidden: hide });
Expand All @@ -50,11 +35,5 @@ export const deleteShoutout = async (uuid: string, user: IdolMember): Promise<vo
if (!shoutout) {
throw new NotFoundError(`No shoutout with id '${uuid}' found.`);
}
const isLeadOrAdmin = await PermissionsManager.isLeadOrAdmin(user);
if (!isLeadOrAdmin && shoutout.giver.email !== user.email) {
throw new PermissionError(
`You are not a lead or admin, so you can't delete a shoutout from a different user!`
);
}
await shoutoutsDao.deleteShoutout(uuid);
};
14 changes: 0 additions & 14 deletions backend/src/API/siteIntegrationAPI.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Octokit } from '@octokit/rest';
import { PRResponse } from '../types/GithubTypes';
import PermissionsManager from '../utils/permissionsManager';
import { PermissionError, BadRequestError } from '../utils/errors';

require('dotenv').config();

export const requestIDOLPullDispatch = async (user: IdolMember): Promise<{ updated: boolean }> => {
await checkPermissions(user);
const octokit = new Octokit({
auth: `token ${process.env.BOT_TOKEN}`,
userAgent: 'cornell-dti/idol-backend'
Expand All @@ -20,15 +18,13 @@ export const requestIDOLPullDispatch = async (user: IdolMember): Promise<{ updat
};

export const getIDOLChangesPR = async (user: IdolMember): Promise<{ pr: PRResponse }> => {
await checkPermissions(user);
const foundPR = await findBotPR();
return { pr: foundPR };
};

export const acceptIDOLChanges = async (
user: IdolMember
): Promise<{ pr: PRResponse; merged: boolean }> => {
await checkPermissions(user);
const octokit = new Octokit({
auth: `token ${process.env.BOT_TOKEN}`,
userAgent: 'cornell-dti/idol-backend'
Expand Down Expand Up @@ -62,7 +58,6 @@ export const acceptIDOLChanges = async (
export const rejectIDOLChanges = async (
user: IdolMember
): Promise<{ pr: PRResponse; closed: boolean }> => {
await checkPermissions(user);
const foundPR = await findBotPR();
const octokit2 = new Octokit({
auth: `token ${process.env.BOT_2_TOKEN}`,
Expand Down Expand Up @@ -99,12 +94,3 @@ const findBotPR = async (): Promise<PRResponse> => {
}
return foundPR;
};

const checkPermissions = async (user: IdolMember): Promise<void> => {
const canEdit = await PermissionsManager.canDeploySite(user);
if (!canEdit) {
throw new PermissionError(
`User with email: ${user.email} does not have permission to trigger site deploys!`
);
}
};
15 changes: 1 addition & 14 deletions backend/src/API/teamAPI.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { v4 as uuidv4 } from 'uuid';
import PermissionsManager from '../utils/permissionsManager';
import { Team } from '../types/DataTypes';
import { BadRequestError, PermissionError } from '../utils/errors';
import { BadRequestError } from '../utils/errors';
import MembersDao from '../dao/MembersDao';

const membersDao = new MembersDao();
Expand Down Expand Up @@ -93,12 +92,6 @@ const updateFormerMembers = async (team: Team, oldTeam: Team): Promise<void> =>
};

export const setTeam = async (teamBody: Team, member: IdolMember): Promise<Team> => {
const canEdit = await PermissionsManager.canEditTeams(member);
if (!canEdit) {
throw new PermissionError(
`User with email: ${member.email} does not have permission to edit teams!`
);
}
if (teamBody.members.length > 0 && !teamBody.members[0].email) {
throw new BadRequestError('Malformed members on POST!');
}
Expand All @@ -110,12 +103,6 @@ export const deleteTeam = async (teamBody: Team, member: IdolMember): Promise<Te
if (!teamBody.uuid || teamBody.uuid === '') {
throw new BadRequestError("Couldn't delete team with undefined uuid!");
}
const canEdit = await PermissionsManager.canEditTeams(member);
if (!canEdit) {
throw new PermissionError(
`User with email: ${member.email} does not have permission to delete teams!`
);
}
await updateTeamMembers({ ...teamBody, members: [], leaders: [], formerMembers: [] });
return teamBody;
};
Loading