add organizationRoutes

This commit is contained in:
Alessandro Ferro 2024-02-20 22:25:04 +01:00
parent 1a233859ba
commit f7f6c741c4
3 changed files with 263 additions and 244 deletions

View File

@ -17,6 +17,7 @@ const cors = require('cors');
const rateLimit = require('express-rate-limit');
const apiController = require('./controllers/api_controller.js'); // todo refactor
const personRoutes = require('./routes/person_routes.js');
const organizationRoutes = require('./routes/organization_routes.js');
const jwt_utils = require('./utils/jwt_utils.js');
require('dotenv').config();
@ -42,10 +43,10 @@ protectedRoutes.put('/person/:id', personRoutes.updatePerson);
protectedRoutes.delete('/person/delete', personRoutes.deletePerson);
protectedRoutes.post('/organization/admin', apiController.addOrganizationAdmin);
protectedRoutes.delete('/organization/removeadmin', apiController.removeOrganizationAdmin);
protectedRoutes.post('/organization', apiController.createOrganization);
protectedRoutes.get('/organization/:id', apiController.getOrganization);
protectedRoutes.put('/organization/:id', apiController.updateOrganization);
protectedRoutes.delete('/organization/:id', apiController.deleteOrganization);
protectedRoutes.post('/organization', organizationRoutes.createOrganization);
protectedRoutes.get('/organization/:id', organizationRoutes.getOrganization);
protectedRoutes.put('/organization/:id', organizationRoutes.updateOrganization);
protectedRoutes.delete('/organization/:id', organizationRoutes.deleteOrganization);
protectedRoutes.post('/organization/post', apiController.createOrganizationPost);
protectedRoutes.delete('/organization/post/:id', apiController.deleteOrganizationPost);

View File

@ -16,248 +16,49 @@ const knex = require('../utils/knex_config');
// ======== BEGIN API ENDPOINTS ========
/**
* POST Request
*
* Creates an Organization and its Administrator.
*
* Required field(s): name
*
* @returns the inserted organization
*/
async function createOrganization(req, res){
// Ensure that the required fields are present before proceeding
if (!req.body.name) {
return res.status(400).json({ error : "Invalid request"});
}
try{
const insertedOrganization = await knex.transaction(async (trx) => {
// We have to insert either both in Organization and in OrganizationAdministrator
// or in neither
const organizationResult = await trx('Organization')
.insert({
name: req.body.name,
location: req.body.location,
description: req.body.description,
is_hiring: req.body.is_hiring,
}, '*');
// Inserting in the "OrganizationAdministrator" table
await trx('OrganizationAdministrator')
.insert({
id_person: req.jwt.person_id,
id_organization: organizationResult[0].id,
});
return organizationResult[0];
});
return res.status(200).json({ Organization: insertedOrganization });
}
catch (error){
console.error('Error creating Organization:', error);
res.status(500).json({error : "Internal server error"});
}
}
/**
* PUT Request
* Updates an Organization's details
*
* Required field(s): none.
*/
async function updateOrganization(req, res){
const updateOrganization = {};
if(req.body.name){
updateOrganization.name = req.body.name;
}
if(req.body.location){
updateOrganization.location = req.body.location;
}
if(req.body.description){
updateOrganization.description = req.body.description;
}
if(req.body.is_hiring){
updateOrganization.is_hiring = req.body.is_hiring;
}
if (Object.keys(updateOrganization).length === 0) {
return res.status(400).json({ error : "Bad request. No data to update"});
}
try {
// // const isOrganizationAdmin = await knex('OrganizationAdministrator')
// // .where('id_person', req.jwt.person_id)
// // .where('id_organization', req.params.id)
// // .select('*')
// // .first();
// // // This introduces a Time of check Time of use weakeness
// // // which could'have been fixed by either
// // // 1) Using "whereExists", thanks to the "it's easier to ask for
// // // forgiveness than for permission" padarigm. Or,
// // // 2) Using a serializable transaction.
// // //
// // // The undersigned chose not to follow these approaches because
// // // this does not introduces any serious vulnerability. In this
// // // way it seems more readable.
// // if(!isOrganizationAdmin){
// // return res.status(403).json({error : "Forbidden"});
// // }
// // await knex('Organization')
// // .where('id', req.params.id)
// // .update({
// // name: req.body.name,
// // location: req.body.location,
// // description: req.body.description,
// // is_hiring: req.body.is_hiring
// // });
const updatedRows = await knex('Organization')
.where('id', req.params.id)
.whereExists(function(){
this.select('*')
.from('OrganizationAdministrator')
.where('id_person', req.jwt.person_id)
.where('id_organization', req.params.id)
})
.update({
name: req.body.name,
location: req.body.location,
description: req.body.description,
is_hiring: req.body.is_hiring
});
if(updatedRows == 1){
return res.status(200).json({ success : "true"});
}
else{
return res.status(404).json({error : "Organization either not found or insufficient permissions"});
}
}
catch (error) {
console.log(error);
return res.status(500).json({error : "Internal server error"});
}
}
/**
* DELETE Request
*
* Deletes the specified organization if the logged user is
* one of its administrator
*/
async function deleteOrganization(req, res){
const organizationIdToDelete = req.params.id;
try {
// Delete organization if admin
const deletedRows = await knex('Organization')
.where({ id: organizationIdToDelete })
.whereExists(function(){
this.select('*')
.from('OrganizationAdministrator')
.where('id_person', req.jwt.person_id)
.where('id_organization', organizationIdToDelete)
})
.del();
if(deletedRows == 0){
return res.status(403).json({error: "Forbidden"});
}
else{
return res.status(200).json({success: true});
}
}
catch (error) {
console.error(error);
return res.status(500).json({error : "Internal server error"});
}
}
/**
* POST Request
*
* Creates a Post belonging to an organization
*
* Required field(s): organization_id, content
* @returns the inserted Post
*/
async function createOrganizationPost(req, res){
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.content) {
return res.status(400).json({ error : "Invalid request"});
}
try {
// Check if the current user is a organization's administrator
const isOrganizationAdmin = await knex('OrganizationAdministrator')
.where('id_person', req.jwt.person_id)
.where('id_organization', req.body.organization_id)
.select('*')
.first();
/**
* POST Request
*
* Creates a Post belonging to an organization
*
* Required field(s): organization_id, content
* @returns the inserted Post
*/
async function createOrganizationPost(req, res){
// Non-exploitable TOC/TOU weakness
// For more information https://softwareengineering.stackexchange.com/questions/451038/when-should-i-be-worried-of-time-of-check-time-of-use-vulnerabilities-during-dat
if(!isOrganizationAdmin){
return res.status(403).json({error : "Forbidden"});
// Ensure that the required fields are present before proceeding
if (!req.body.organization_id || !req.body.content) {
return res.status(400).json({ error : "Invalid request"});
}
const organizationPost = await knex('OrganizationPost')
.insert({
organization_id: req.body.organization_id,
content: req.body.content,
original_author: req.jwt.person_id
})
.returning('*');
return res.status(200).json(organizationPost[0]);
}
catch (error) {
console.log(error);
return res.status(500).json({error : "Internal server error"});
}
}
/**
* GET Request
*
* Obtains an organization by its identifier.
*
* Required field(s): none.
*
* @returns the organization.
*/
async function getOrganization(req, res){
const organizationId = req.params.id;
try {
const organization = await knex('Organization')
.where('id', organizationId)
try {
// Check if the current user is a organization's administrator
const isOrganizationAdmin = await knex('OrganizationAdministrator')
.where('id_person', req.jwt.person_id)
.where('id_organization', req.body.organization_id)
.select('*')
.first();
if(organization) {
return res.status(200).json(organization);
}
else{
return res.status(404).json({error : "Not found"});
// Non-exploitable TOC/TOU weakness
// For more information https://softwareengineering.stackexchange.com/questions/451038/when-should-i-be-worried-of-time-of-check-time-of-use-vulnerabilities-during-dat
if(!isOrganizationAdmin){
return res.status(403).json({error : "Forbidden"});
}
const organizationPost = await knex('OrganizationPost')
.insert({
organization_id: req.body.organization_id,
content: req.body.content,
original_author: req.jwt.person_id
})
.returning('*');
return res.status(200).json(organizationPost[0]);
}
catch (error) {
console.log(error);
return res.status(500).json({error : "Internal server error"});
}
}
catch (error) {
console.error("Error retrieving an organization: " + error);
return res.status(500).json({error : "Internal server error"});
}
}
/**
* DELETE Request
@ -390,10 +191,6 @@ async function removeOrganizationAdmin(req, res){
// means making a JavaScript function defined in one
// module available for use in another module.
module.exports = {
createOrganization,
getOrganization,
updateOrganization,
deleteOrganization,
createOrganizationPost,
deleteOrganizationPost,
addOrganizationAdmin,

View File

@ -0,0 +1,221 @@
/*
This code is part of Blink
licensed under GPLv3
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
const knex = require('../utils/knex_config');
/**
* POST Request
*
* Creates an Organization and its Administrator.
*
* Required field(s): name
*
* @returns the inserted organization
*/
async function createOrganization(req, res){
// Ensure that the required fields are present before proceeding
if (!req.body.name) {
return res.status(400).json({ error : "Invalid request"});
}
try{
const insertedOrganization = await knex.transaction(async (trx) => {
// We have to insert either both in Organization and in OrganizationAdministrator
// or in neither
const organizationResult = await trx('Organization')
.insert({
name: req.body.name,
location: req.body.location,
description: req.body.description,
is_hiring: req.body.is_hiring,
}, '*');
// Inserting in the "OrganizationAdministrator" table
await trx('OrganizationAdministrator')
.insert({
id_person: req.jwt.person_id,
id_organization: organizationResult[0].id,
});
return organizationResult[0];
});
return res.status(200).json({ Organization: insertedOrganization });
}
catch (error){
console.error('Error creating Organization:', error);
res.status(500).json({error : "Internal server error"});
}
}
/**
* PUT Request
* Updates an Organization's details
*
* Required field(s): none.
*/
async function updateOrganization(req, res){
const updateOrganization = {};
if(req.body.name){
updateOrganization.name = req.body.name;
}
if(req.body.location){
updateOrganization.location = req.body.location;
}
if(req.body.description){
updateOrganization.description = req.body.description;
}
if(req.body.is_hiring){
updateOrganization.is_hiring = req.body.is_hiring;
}
if (Object.keys(updateOrganization).length === 0) {
return res.status(400).json({ error : "Bad request. No data to update"});
}
try {
// // const isOrganizationAdmin = await knex('OrganizationAdministrator')
// // .where('id_person', req.jwt.person_id)
// // .where('id_organization', req.params.id)
// // .select('*')
// // .first();
// // // This introduces a Time of check Time of use weakeness
// // // which could'have been fixed by either
// // // 1) Using "whereExists", thanks to the "it's easier to ask for
// // // forgiveness than for permission" padarigm. Or,
// // // 2) Using a serializable transaction.
// // //
// // // The undersigned chose not to follow these approaches because
// // // this does not introduces any serious vulnerability. In this
// // // way it seems more readable.
// // if(!isOrganizationAdmin){
// // return res.status(403).json({error : "Forbidden"});
// // }
// // await knex('Organization')
// // .where('id', req.params.id)
// // .update({
// // name: req.body.name,
// // location: req.body.location,
// // description: req.body.description,
// // is_hiring: req.body.is_hiring
// // });
const updatedRows = await knex('Organization')
.where('id', req.params.id)
.whereExists(function(){
this.select('*')
.from('OrganizationAdministrator')
.where('id_person', req.jwt.person_id)
.where('id_organization', req.params.id)
})
.update({
name: req.body.name,
location: req.body.location,
description: req.body.description,
is_hiring: req.body.is_hiring
});
if(updatedRows == 1){
return res.status(200).json({ success : "true"});
}
else{
return res.status(404).json({error : "Organization either not found or insufficient permissions"});
}
}
catch (error) {
console.log(error);
return res.status(500).json({error : "Internal server error"});
}
}
/**
* DELETE Request
*
* Deletes the specified organization if the logged user is
* one of its administrator
*/
async function deleteOrganization(req, res){
const organizationIdToDelete = req.params.id;
try {
// Delete organization if admin
const deletedRows = await knex('Organization')
.where({ id: organizationIdToDelete })
.whereExists(function(){
this.select('*')
.from('OrganizationAdministrator')
.where('id_person', req.jwt.person_id)
.where('id_organization', organizationIdToDelete)
})
.del();
if(deletedRows == 0){
return res.status(403).json({error: "Forbidden"});
}
else{
return res.status(200).json({success: true});
}
}
catch (error) {
console.error(error);
return res.status(500).json({error : "Internal server error"});
}
}
/**
* GET Request
*
* Obtains an organization by its identifier.
*
* Required field(s): none.
*
* @returns the organization.
*/
async function getOrganization(req, res){
const organizationId = req.params.id;
try {
const organization = await knex('Organization')
.where('id', organizationId)
.select('*')
.first();
if(organization) {
return res.status(200).json(organization);
}
else{
return res.status(404).json({error : "Not found"});
}
}
catch (error) {
console.error("Error retrieving an organization: " + error);
return res.status(500).json({error : "Internal server error"});
}
}
module.exports = {
createOrganization,
getOrganization,
updateOrganization,
deleteOrganization
};