Skip to content

Commit

Permalink
Feat: Resolve merge conflcits in ProjectForm
Browse files Browse the repository at this point in the history
* 1086/security issues verified (hackforla#1521)

* add verification check before mutations

* remove log

* cleanup unused imports

* update tests

* update Update and Read tests

* move vble to env file

* undo secret removal

---------

Co-authored-by: Brad <brad@brads-mbp.lan>
Co-authored-by: Trillium S <Spiteless@gmail.com>
  • Loading branch information
3 people committed Mar 4, 2024
1 parent 44f0574 commit b16cbea
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 96 deletions.
2 changes: 1 addition & 1 deletion backend/middleware/user.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function isAdminByEmail(req, res, next) {
return res.sendStatus(400);
} else {
const role = user.accessLevel;
if (req.get('origin').includes('3001') || role === 'admin' || user.managedProjects.length > 0) {
if (role === 'admin' || user.managedProjects.length > 0) {
next();
} else {
next(res.sendStatus(401));
Expand Down
7 changes: 4 additions & 3 deletions backend/routers/projects.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ const express = require("express");
const router = express.Router();

const { ProjectController } = require('../controllers');
const { AuthUtil } = require("../middleware");

// The base is /api/projects
router.get('/', ProjectController.project_list);

router.post('/', ProjectController.create);
router.post('/', AuthUtil.verifyCookie, ProjectController.create);

router.get('/:ProjectId', ProjectController.project_by_id);

router.put('/:ProjectId', ProjectController.update);
router.put('/:ProjectId', AuthUtil.verifyCookie, ProjectController.update);

router.patch('/:ProjectId', ProjectController.update);
router.patch('/:ProjectId', AuthUtil.verifyCookie, ProjectController.update);


module.exports = router;
148 changes: 142 additions & 6 deletions backend/routers/projects.router.test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,58 @@
const supertest = require('supertest');
const app = require('../app');
const request = supertest(app);
const jwt = require('jsonwebtoken');
const { CONFIG_AUTH } = require('../config');


const { setupDB } = require('../setup-test');
setupDB('api-projects');

const { Project } = require('../models');
const { Project, User } = require('../models');
const CONFIG = require('../config/auth.config');

const headers = {};
headers['x-customrequired-header'] = CONFIG.CUSTOM_REQUEST_HEADER;
headers.Accept = 'application/json';
headers.authorization = 'Bearer sometoken';

let token;


describe('CREATE', () => {
beforeAll( async () => {
const submittedData = {
name: {
firstName: 'test',
lastName: 'user',
},
email: 'newtest@test.com',
};
const user = await User.create(submittedData);
const auth_origin = 'TEST';
token = jwt.sign(
{ id: user.id, role: user.accessLevel, auth_origin },
CONFIG_AUTH.SECRET,
{
expiresIn: `${CONFIG_AUTH.TOKEN_EXPIRATION_SEC}s`,
},
);
})
test('Create a Project with POST to /api/projects/ without token', async (done) => {
// Test Data
const submittedData = {
name: 'projectName',
};

// Submit a project
const res = await request
.post('/api/projects/')
.set(headers)
.send(submittedData);
expect(res.status).toBe(401);
done();
});

test('Create a Project with POST to /api/projects/', async (done) => {
// Test Data
const submittedData = {
Expand All @@ -23,6 +63,7 @@ describe('CREATE', () => {
const res = await request
.post('/api/projects/')
.set(headers)
.set('Cookie', [`token=${token}`] )
.send(submittedData);
expect(res.status).toBe(201);
done();
Expand All @@ -40,6 +81,7 @@ describe('READ', () => {
const res = await request
.post('/api/projects/')
.set(headers)
.set('Cookie', [`token=${token}`])
.send(submittedData);
expect(res.status).toBe(201);

Expand All @@ -54,7 +96,25 @@ describe('READ', () => {
});

describe('UPDATE', () => {
test('Update a project with PATCH to /api/projects/:id', async (done) => {
beforeAll(async () => {
const submittedData = {
name: {
firstName: 'test',
lastName: 'user',
},
email: 'newtest@test.com',
};
const user = await User.create(submittedData);
const auth_origin = 'TEST';
token = jwt.sign(
{ id: user.id, role: user.accessLevel, auth_origin },
CONFIG_AUTH.SECRET,
{
expiresIn: `${CONFIG_AUTH.TOKEN_EXPIRATION_SEC}s`,
},
);
})
test('Update a project with PATCH to /api/projects/:id without a token', async (done) => {
// Test Data
const submittedData = {
name: 'projectName',
Expand All @@ -64,6 +124,7 @@ describe('UPDATE', () => {
const res = await request
.post('/api/projects/')
.set(headers)
.set('Cookie', [`token=${token}`])
.send(submittedData);
expect(res.status).toBe(201);

Expand All @@ -76,10 +137,44 @@ describe('UPDATE', () => {
.put(`/api/projects/${res.body._id}`)
.set(headers)
.send(updatedDataPayload);
expect(res2.status).toBe(200);
expect(res2.status).toBe(401);

// Get project
const res3 = await request.get(`/api/projects/${res.body._id}`)
.set(headers);
expect(res3.status).toBe(200);
done();
});
test('Update a project with PATCH to /api/projects/:id with a token', async (done) => {
// Test Data
const submittedData = {
name: 'projectName',
};

// Submit a project
const res = await request
.post('/api/projects/')
.set(headers)
.set('Cookie', [`token=${token}`])
.send(submittedData);
expect(res.status).toBe(201);

const updatedDataPayload = {
name: 'updatedProjectName',
};

// Update project
const res2 = await request
.put(`/api/projects/${res.body._id}`)
.set(headers)
.set('Cookie', [`token=${token}`])
.send(updatedDataPayload);
expect(res2.status).toBe(200)

// Get project
const res3 = await request.get(`/api/projects/${res.body._id}`).set(headers);
const res3 = await request.get(`/api/projects/${res.body._id}`)
.set(headers)
.set('Cookie', [`token=${token}`])
expect(res3.status).toBe(200);

const APIData = res3.body;
Expand All @@ -89,7 +184,45 @@ describe('UPDATE', () => {
});

describe('DELETE', () => {
test('Delete a project with POST to /api/projects/:id', async (done) => {
beforeAll(async () => {
const submittedData = {
name: {
firstName: 'test',
lastName: 'user',
},
email: 'newtest@test.com',
};
const user = await User.create(submittedData);
const auth_origin = 'TEST';
token = jwt.sign(
{ id: user.id, role: user.accessLevel, auth_origin },
CONFIG_AUTH.SECRET,
{
expiresIn: `${CONFIG_AUTH.TOKEN_EXPIRATION_SEC}s`,
},
);
})
test('Delete a project with POST to /api/projects/:id without a token', async (done) => {
// Test Data
const submittedData = {
name: 'projectName',
};

// Submit a project
const res = await request
.post('/api/projects/')
.set(headers)
.set('Cookie', [`token=${token}`])
.send(submittedData);
expect(res.status).toBe(201);

// Delete project
const res2 = await request.patch(`/api/projects/${res.body._id}`)
.set(headers);
expect(res2.status).toBe(401);
done();
});
test('Delete a project with POST to /api/projects/:id with a token', async (done) => {
// Test Data
const submittedData = {
name: 'projectName',
Expand All @@ -99,11 +232,14 @@ describe('DELETE', () => {
const res = await request
.post('/api/projects/')
.set(headers)
.set('Cookie', [`token=${token}`])
.send(submittedData);
expect(res.status).toBe(201);

// Delete project
const res2 = await request.patch(`/api/projects/${res.body._id}`).set(headers);
const res2 = await request.patch(`/api/projects/${res.body._id}`)
.set(headers)
.set('Cookie', [`token=${token}`])
expect(res2.status).toBe(200);
done();
});
Expand Down
7 changes: 4 additions & 3 deletions backend/routers/recurringEvents.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const cors = require('cors');

const { RecurringEvent } = require('../models/recurringEvent.model');
const { RecurringEventController } = require('../controllers/');
const { AuthUtil } = require('../middleware');

// GET /api/recurringevents/
router.get('/', cors(), (req, res) => {
Expand Down Expand Up @@ -34,10 +35,10 @@ router.get('/:id', (req, res) => {
});
});

router.post('/', RecurringEventController.create);
router.post('/', AuthUtil.verifyCookie, RecurringEventController.create);

router.patch('/:RecurringEventId', RecurringEventController.update);
router.patch('/:RecurringEventId', AuthUtil.verifyCookie, RecurringEventController.update);

router.delete('/:RecurringEventId', RecurringEventController.destroy);
router.delete('/:RecurringEventId', AuthUtil.verifyCookie, RecurringEventController.destroy);

module.exports = router;
2 changes: 0 additions & 2 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ import HealthCheck from './pages/HealthCheck';
import SecretPassword from './pages/SecretPassword';
import UserWelcome from './pages/UserWelcome';

import ProjectForm from './components/ProjectForm';

import { ThemeProvider } from '@mui/material';
import theme from './theme';

Expand Down
79 changes: 1 addition & 78 deletions client/src/components/ProjectForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from '@mui/material';
import { styled } from '@mui/material/styles';
import useAuth from "../hooks/useAuth"
import TitledBox from './TitledBox';

/** Project Form Component
*
Expand Down Expand Up @@ -157,8 +158,6 @@ export default function ProjectForm() {
setActiveButton('close');
};

<<<<<<< HEAD
=======
// ----------------- Icons -----------------

// Holds the Add New Project Icon and styling.
Expand Down Expand Up @@ -202,7 +201,6 @@ export default function ProjectForm() {
// ----------------- Location radio -----------------

// Holdes the location radios styling.
>>>>>>> 08e0b51 (feat: Refactor ProjectForm to use TitledBox)
const locationRadios = (
<Grid item>
<FormControl>
Expand Down Expand Up @@ -235,80 +233,6 @@ export default function ProjectForm() {
<Box sx={{ textAlign: 'center' }}>
<Typography variant="h1">Project Management</Typography>
</Box>
<<<<<<< HEAD
<Box sx={{ bgcolor: '#F5F5F5' }}>
<Box sx={{ p: 2, display: 'flex', justifyContent: 'space-between' }}>
<Box>
<Typography sx={{ fontSize: '18px', fontWeight: '600' }}>
Project Information
</Typography>
</Box>
<Box sx={{ display: 'flex' }}>
<PlusIcon style={{ marginRight: '7px' }} />
<Typography sx={{ fontSize: '14px', fontWeight: '600' }}>
Add New Project
</Typography>
</Box>
</Box>
<Divider sx={{ borderColor: 'rgba(0,0,0,1)' }} />
<Box sx={{ py: 2, px: 4 }}>
<form id="project-form" onSubmit={handleSubmit((data) => {
submitForm(data)
})}>
{simpleInputs.map((input) => (
<Box sx={{ mb: 1 }} key={input.name}>
<Grid container alignItems="center">
<Grid item xs="auto" sx={{ pr: 3 }}>
<InputLabel
sx={{ width: 'max-content', ml: 0.5, mb: 0.5 }}
id={input.name}
>
{input.label}
</InputLabel>
</Grid>
{input.name === 'location' && locationRadios}
</Grid>
<TextField
error={!!errors[input.name]}
type={input.type}
{...register(input.name, {required: `${input.name} is required` ,
pattern: input.name === 'location' ?
locationType === 'remote' ?
{value: input.value, message: input.errorMessage} :
{value: input.addressValue, message: input.addressError} :
{value: input.value, message: input.errorMessage} } )}
placeholder={input.placeholder}
helperText={`${errors[input.name]?.message || ' '}`}
/>
</Box>
))}
</form>
</Box>
</Box>
<Box>
<Grid container justifyContent="space-evenly" sx={{ my: 3 }}>
<Grid item xs="auto">
<StyledButton
type="submit"
form="project-form"
variant={activeButton === 'save' ? 'contained' : 'secondary'}
>
Save
</StyledButton>
</Grid>
<Grid item xs="auto">
<StyledButton
component={Link}
to="/projects"
variant={activeButton === 'close' ? 'contained' : 'secondary'}
disabled={activeButton !== 'close'}
>
Close
</StyledButton>
</Grid>
</Grid>
</Box>
=======
<TitledBox
title={editMode ? 'Editing Project' : 'Project Information'}
badge={isEdit ? editIcon() : addIcon()}
Expand Down Expand Up @@ -358,7 +282,6 @@ export default function ProjectForm() {
</Grid>
</Box>
</TitledBox>
>>>>>>> 08e0b51 (feat: Refactor ProjectForm to use TitledBox)
</Box>
) : (
<Redirect to="/login" />
Expand Down
Loading

0 comments on commit b16cbea

Please sign in to comment.