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 27 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
14 changes: 14 additions & 0 deletions backend/rbac.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"resources": {
"dev-portfolio": {
"attributes": ["meta_only"],
"read_only": ["lead"],
"read_and_write": ["dev_lead"]
},
"dev-portfolio-submission": {
"attributes": ["email"],
"read_only": ["lead"],
"read_and_write": ["dev_lead"]
}
}
}
33 changes: 33 additions & 0 deletions backend/src/API/candidateDeciderAPI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { Router } from 'express';
import CandidateDeciderDao from '../dao/CandidateDeciderDao';
import { NotFoundError, PermissionError } from '../utils/errors';
import PermissionsManager from '../utils/permissionsManager';
import {
loginCheckedDelete,
loginCheckedGet,
loginCheckedPost,
loginCheckedPut
} from '../utils/auth';

const candidateDeciderDao = new CandidateDeciderDao();

Expand Down Expand Up @@ -142,3 +149,29 @@ export const updateCandidateDeciderComment = async (
};
candidateDeciderDao.updateInstance(updatedInstance);
};

export const candidateDeciderRouter = Router();

loginCheckedGet(candidateDeciderRouter, '/', async (_, user) => ({
instances: await getAllCandidateDeciderInstances(user)
}));
loginCheckedGet(candidateDeciderRouter, '/:uuid', async (req, user) => ({
instance: await getCandidateDeciderInstance(req.params.uuid, user)
}));
loginCheckedPost(candidateDeciderRouter, '/', async (req, user) => ({
instance: await createNewCandidateDeciderInstance(req.body, user)
}));
loginCheckedPut(candidateDeciderRouter, '/:uuid', async (req, user) =>
toggleCandidateDeciderInstance(req.params.uuid, user).then(() => ({}))
);
loginCheckedDelete(candidateDeciderRouter, '/:uuid', async (req, user) =>
deleteCandidateDeciderInstance(req.params.uuid, user).then(() => ({}))
);
loginCheckedPut(candidateDeciderRouter, '/:uuid/rating', (req, user) =>
updateCandidateDeciderRating(user, req.params.uuid, req.body.id, req.body.rating).then(() => ({}))
);
loginCheckedPost(candidateDeciderRouter, '/decider/:uuid/comment', (req, user) =>
updateCandidateDeciderComment(user, req.params.uuid, req.body.id, req.body.comment).then(
() => ({})
)
);
86 changes: 86 additions & 0 deletions backend/src/API/devPortfolioAPI.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { Router } from 'express';
import { DateTime } from 'luxon';
import DevPortfolioDao from '../dao/DevPortfolioDao';
import PermissionsManager from '../utils/permissionsManager';
import { PermissionError, BadRequestError, NotFoundError } from '../utils/errors';
import { validateSubmission, isWithinDates } from '../utils/githubUtil';
import {
loginCheckedDelete,
loginCheckedGet,
loginCheckedPost,
loginCheckedPut
} from '../utils/auth';
import DPSubmissionRequestLogDao from '../dao/DPSubmissionRequestLogDao';

const zonedTime = (timestamp: number, ianatz = 'America/New_York') =>
DateTime.fromMillis(timestamp, { zone: ianatz });
Expand Down Expand Up @@ -158,3 +166,81 @@ export const regradeSubmissions = async (uuid: string, user: IdolMember): Promis
await devPortfolioDao.updateInstance(updatedDP);
return updatedDP;
};

export const devPortfolioRouter = Router();
const devPortfolioSubmissionRouter = Router({ mergeParams: true });
devPortfolioRouter.use('/:uuid/submission', devPortfolioSubmissionRouter);

// /dev-portfolio
loginCheckedGet(
devPortfolioRouter,
'/',
async (req, user) => ({
portfolios: !req.query.meta_only
? await getAllDevPortfolios(user)
: await getAllDevPortfolioInfo()
}),
'dev-portfolio'
);
loginCheckedGet(
devPortfolioRouter,
'/:uuid',
async (req, user) => ({
portfolio: !req.query.meta_only
? await getDevPortfolio(req.params.uuid, user)
: await getDevPortfolioInfo(req.params.uuid)
}),
'dev-portfolio'
);

loginCheckedPost(
devPortfolioRouter,
'/',
async (req, user) => ({
portfolio: await createNewDevPortfolio(req.body, user)
}),
'dev-portfolio'
);
loginCheckedDelete(
devPortfolioRouter,
'/:uuid',
async (req, user) => deleteDevPortfolio(req.params.uuid, user).then(() => ({})),
'dev-portfolio'
);

// devPortfolioSubmissionRouter: /dev-portfolio/:uuid/submission
loginCheckedPost(
devPortfolioSubmissionRouter,
'/',
async (req, user) => {
await DPSubmissionRequestLogDao.logRequest(user.email, req.body.uuid, req.body.submission);
return {
submission: await makeDevPortfolioSubmission(req.body.uuid, req.body.submission)
};
},
'dev-portfolio-submission'
);
loginCheckedPut(
devPortfolioSubmissionRouter,
'/regrade',
async (req, user) => ({
portfolio: await regradeSubmissions(req.body.uuid, user)
}),
'dev-portfolio-submission'
);
loginCheckedPut(
devPortfolioSubmissionRouter,
'/',
async (req, user) => ({
portfolio: await updateSubmissions(req.params.uuid, req.body.updatedSubmissions, user)
}),
'dev-portfolio-submission'
);
loginCheckedGet(
devPortfolioSubmissionRouter,
'/:email',
async (req, user) => ({
submissions: await getUsersDevPortfolioSubmissions(req.params.uuid, user)
}),
'dev-portfolio-submission'
);
17 changes: 17 additions & 0 deletions backend/src/API/imageAPI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Router } from 'express';
import { bucket } from '../firebase';
import { getNetIDFromEmail, filterImagesResponse } from '../utils/memberUtil';
import { NotFoundError } from '../utils/errors';
import { loginCheckedGet } from '../utils/auth';

export const allMemberImages = async (): Promise<readonly ProfileImage[]> => {
const files = await bucket.getFiles({ prefix: 'images/' });
Expand Down Expand Up @@ -44,3 +46,18 @@ export const getMemberImage = async (user: IdolMember): Promise<string> => {
});
return signedUrl[0];
};

export const memberImageRouter = Router();

loginCheckedGet(memberImageRouter, '/:email', async (_, user) => ({
url: await getMemberImage(user)
}));

loginCheckedGet(memberImageRouter, '/signedURL', async (_, user) => ({
url: await setMemberImage(user)
}));

memberImageRouter.get('/', async (_, res) => {
const images = await allMemberImages();
res.status(200).json({ images });
});
45 changes: 44 additions & 1 deletion backend/src/API/memberAPI.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { Request } from 'express';
import { Request, Router } 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';
import { sendMemberUpdateNotifications } from './mailAPI';
import {
loginCheckedDelete,
loginCheckedGet,
loginCheckedPost,
loginCheckedPut
} from '../utils/auth';

const membersDao = new MembersDao();

Expand Down Expand Up @@ -119,3 +125,40 @@ export const reviewUserInformationChange = async (
MembersDao.revertMemberInformationChanges(rejected)
]);
};

export const memberRouter = Router();
export const memberDiffRouter = Router();

memberRouter.get('/', async (req, res) => {
const type = req.query.type as string | undefined;
let members;
switch (type) {
case 'all-semesters':
members = await MembersDao.getMembersFromAllSemesters();
break;
case 'approved':
members = await allApprovedMembers();
break;
default:
members = await allMembers();
}
res.status(200).json({ members });
});

loginCheckedPost(memberRouter, '/', async (req, user) => ({
member: await setMember(req.body, user)
}));
loginCheckedDelete(memberRouter, '/:email', async (req, user) => {
await deleteMember(req.params.email, user);
return {};
});
loginCheckedPut(memberRouter, '/', async (req, user) => ({
member: await updateMember(req, req.body, user)
}));

loginCheckedGet(memberDiffRouter, '/', async (_, user) => ({
diffs: await getUserInformationDifference(user)
}));
loginCheckedPut(memberDiffRouter, '/', async (req, user) => ({
member: await reviewUserInformationChange(req.body.approved, req.body.rejected, user)
}));
31 changes: 31 additions & 0 deletions backend/src/API/shoutoutAPI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { Router } from 'express';
import PermissionsManager from '../utils/permissionsManager';
import { NotFoundError, PermissionError } from '../utils/errors';
import ShoutoutsDao from '../dao/ShoutoutsDao';
import {
loginCheckedGet,
loginCheckedPost,
loginCheckedPut,
loginCheckedDelete
} from '../utils/auth';

const shoutoutsDao = new ShoutoutsDao();

Expand Down Expand Up @@ -58,3 +65,27 @@ export const deleteShoutout = async (uuid: string, user: IdolMember): Promise<vo
}
await shoutoutsDao.deleteShoutout(uuid);
};

export const shoutoutRouter = Router();

loginCheckedGet(shoutoutRouter, '/:email/:type', async (req, user) => ({
shoutouts: await getShoutouts(req.params.email, req.params.type as 'given' | 'received', user)
}));

loginCheckedGet(shoutoutRouter, '/', async () => ({
shoutouts: await getAllShoutouts()
}));

loginCheckedPost(shoutoutRouter, '/', async (req, user) => ({
shoutout: await giveShoutout(req.body, user)
}));

loginCheckedPut(shoutoutRouter, '/', async (req, user) => {
await hideShoutout(req.body.uuid, req.body.hide, user);
return {};
});

loginCheckedDelete(shoutoutRouter, '/:uuid', async (req, user) => {
await deleteShoutout(req.params.uuid, user);
return {};
});
24 changes: 24 additions & 0 deletions backend/src/API/signInFormAPI.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Router } from 'express';
import SignInFormDao from '../dao/SignInFormDao';
import { PermissionError, BadRequestError, NotFoundError } from '../utils/errors';
import { signInFormCollection, memberCollection } from '../firebase';
import PermissionsManager from '../utils/permissionsManager';
import { loginCheckedGet, loginCheckedPost } from '../utils/auth';

const checkIfDocExists = async (id: string): Promise<boolean> =>
(await signInFormCollection.doc(id).get()).exists;
Expand Down Expand Up @@ -87,3 +89,25 @@ export const getSignInPrompt = async (id: string): Promise<string | undefined> =
if (!signInForm) throw new NotFoundError(`Sign-in form with id ${id} does not exist!`);
return signInForm.prompt;
};

export const signInRouter = Router();
loginCheckedPost(signInRouter, '/signInExists', async (req, _) => ({
exists: await signInFormExists(req.body.id)
}));
loginCheckedPost(signInRouter, '/signInExpired', async (req, _) => ({
expired: await signInFormExpired(req.body.id)
}));
loginCheckedPost(signInRouter, '/signInCreate', async (req, user) =>
createSignInForm(req.body.id, req.body.expireAt, req.body.prompt, user)
);
loginCheckedPost(signInRouter, '/signInDelete', async (req, user) => {
await deleteSignInForm(req.body.id, user);
return {};
});
loginCheckedPost(signInRouter, '/signIn', async (req, user) =>
signIn(req.body.id, req.body.response, user)
);
loginCheckedPost(signInRouter, '/signInAll', async (_, user) => allSignInForms(user));
loginCheckedGet(signInRouter, '/signInPrompt/:id', async (req, _) => ({
prompt: await getSignInPrompt(req.params.id)
}));
14 changes: 14 additions & 0 deletions backend/src/API/siteIntegrationAPI.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Router } from 'express';
import { Octokit } from '@octokit/rest';
import { PRResponse } from '../types/GithubTypes';
import PermissionsManager from '../utils/permissionsManager';
import { PermissionError, BadRequestError } from '../utils/errors';
import { loginCheckedGet, loginCheckedPost } from '../utils/auth';

require('dotenv').config();

Expand Down Expand Up @@ -108,3 +110,15 @@ const checkPermissions = async (user: IdolMember): Promise<void> => {
);
}
};

export const siteIntegrationRouter = Router();

loginCheckedPost(siteIntegrationRouter, '/pullIDOLChanges', (_, user) =>
requestIDOLPullDispatch(user)
);

loginCheckedGet(siteIntegrationRouter, '/getIDOLChangesPR', (_, user) => getIDOLChangesPR(user));

loginCheckedPost(siteIntegrationRouter, '/acceptIDOLChanges', (_, user) => acceptIDOLChanges(user));

loginCheckedPost(siteIntegrationRouter, '/rejectIDOLChanges', (_, user) => rejectIDOLChanges(user));
15 changes: 15 additions & 0 deletions backend/src/API/teamAPI.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Router } from 'express';
import { v4 as uuidv4 } from 'uuid';
import PermissionsManager from '../utils/permissionsManager';
import { Team } from '../types/DataTypes';
import { BadRequestError, PermissionError } from '../utils/errors';
import MembersDao from '../dao/MembersDao';
import { loginCheckedGet, loginCheckedPost, loginCheckedPut } from '../utils/auth';

const membersDao = new MembersDao();

Expand Down Expand Up @@ -119,3 +121,16 @@ export const deleteTeam = async (teamBody: Team, member: IdolMember): Promise<Te
await updateTeamMembers({ ...teamBody, members: [], leaders: [], formerMembers: [] });
return teamBody;
};

export const teamRouter = Router();

loginCheckedGet(teamRouter, '/', async () => ({ teams: await allTeams() }));

loginCheckedPut(teamRouter, '/', async (req, user) => ({
team: await setTeam(req.body, user)
}));

// TODO: should eventually make this a delete request
loginCheckedPost(teamRouter, '/', async (req, user) => ({
team: await deleteTeam(req.body, user)
}));
Loading