diff --git a/backend/apis/nodejs/src/app.js b/backend/apis/nodejs/src/app.js index ab8bca9..7ec4504 100644 --- a/backend/apis/nodejs/src/app.js +++ b/backend/apis/nodejs/src/app.js @@ -2,11 +2,11 @@ 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 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 + 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. */ @@ -20,10 +20,9 @@ const rateLimit = require('express-rate-limit'); const personRoutes = require('./routes/person_routes.js'); const organizationRoutes = require('./routes/organization_routes.js'); const organizationAdminRoutes = require('./routes/organization_admin_routes.js'); -const organizationPostRoutes = require('./routes/organization_post_routes.js') +const organizationPostRoutes = require('./routes/organization_post_routes.js'); const jwt_utils = require('./utils/jwt_utils.js'); - // Application configuration const app = express(); app.use(express.json()); // Middleware which parses JSON for POST requests @@ -31,7 +30,7 @@ app.use(cors()); // Enable CORS for all routes app.use(rateLimit({ windowMs: process.env.LIMITER_WINDOW, max: process.env.LIMITER_MAXIMUM_PER_WINDOW, - message: {error : "Too many requests from this IP, please try again later"} + message: { error: 'Too many requests from this IP, please try again later' } })); // Apply the rate limiter middleware to all routes const publicRoutes = express.Router(); @@ -63,4 +62,4 @@ app.listen(port, () => { console.log(`Blink API server is running on port ${port}`); }); -module.exports = app; \ No newline at end of file +module.exports = app; diff --git a/backend/apis/nodejs/src/models/organization_admin_model.js b/backend/apis/nodejs/src/models/organization_admin_model.js index 84d66df..fad56f0 100644 --- a/backend/apis/nodejs/src/models/organization_admin_model.js +++ b/backend/apis/nodejs/src/models/organization_admin_model.js @@ -2,11 +2,11 @@ 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 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 + 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. */ @@ -14,69 +14,69 @@ const knex = require('../utils/knex_config'); /** - * Checks whether the specified person is an administrator + * Checks whether the specified person is an administrator * of the specified administrator - * @param {*} personId - * @param {*} organizationId + * @param {*} personId + * @param {*} organizationId * @returns true if administrator, false otherwise */ -async function isPersonAdmin(personId, organizationId){ - const isPersonAdmin = await knex('OrganizationAdministrator') - .where('id_person', personId) - .where('id_organization', organizationId) - .select('*') - .first(); - return isPersonAdmin; +async function isPersonAdmin (personId, organizationId) { + const isPersonAdmin = await knex('OrganizationAdministrator') + .where('id_person', personId) + .where('id_organization', organizationId) + .select('*') + .first(); + return isPersonAdmin; } /** * Add the specified Person as the Organization administrator - * @param {*} personId - * @param {*} organizationId + * @param {*} personId + * @param {*} organizationId */ -async function addOrganizationAdministrator(personId, organizationId){ - await knex('OrganizationAdministrator') - .insert({ - id_person: personId, - id_organization: organizationId - }); +async function addOrganizationAdministrator (personId, organizationId) { + await knex('OrganizationAdministrator') + .insert({ + id_person: personId, + id_organization: organizationId + }); } /** * Remove Person from the Organization's administrators. * If no more Administrators are left, the Organization is removed. - * @param {*} personId - * @param {*} organizationId + * @param {*} personId + * @param {*} organizationId */ -async function removeOrganizationAdmin(personId, organizationId){ - const transaction = await knex.transaction(); - - // We lock the table to ensure that we won't have concurrency issues - // while checking remainingAdministrators. - // TODO: Understand whether a lock on the table is really necessary - await transaction.raw('LOCK TABLE "OrganizationAdministrator" IN SHARE MODE'); - - await transaction('OrganizationAdministrator') - .where('id_person', personId) - .where('id_organization', organizationId) +async function removeOrganizationAdmin (personId, organizationId) { + const transaction = await knex.transaction(); + + // We lock the table to ensure that we won't have concurrency issues + // while checking remainingAdministrators. + // TODO: Understand whether a lock on the table is really necessary + await transaction.raw('LOCK TABLE "OrganizationAdministrator" IN SHARE MODE'); + + await transaction('OrganizationAdministrator') + .where('id_person', personId) + .where('id_organization', organizationId) + .del(); + + // TODO: If the user instead deletes their entire profile, the organization will not be deleted. Fix. (database schema) + const remainingAdministrators = await transaction('OrganizationAdministrator') + .where({ id_organization: organizationId }); + + if (remainingAdministrators.length === 0) { + // If no more users, delete the organization + await transaction('Organization') + .where('id', organizationId) .del(); + } - // TODO: If the user instead deletes their entire profile, the organization will not be deleted. Fix. (database schema) - const remainingAdministrators = await transaction('OrganizationAdministrator') - .where({ id_organization: organizationId }); - - if (remainingAdministrators.length === 0) { - // If no more users, delete the organization - await transaction('Organization') - .where('id', organizationId) - .del(); - } - - await transaction.commit(); + await transaction.commit(); } module.exports = { - isPersonAdmin, - addOrganizationAdministrator, - removeOrganizationAdmin - }; \ No newline at end of file + isPersonAdmin, + addOrganizationAdministrator, + removeOrganizationAdmin +}; diff --git a/backend/apis/nodejs/src/models/organization_model.js b/backend/apis/nodejs/src/models/organization_model.js index 0574565..d92defc 100644 --- a/backend/apis/nodejs/src/models/organization_model.js +++ b/backend/apis/nodejs/src/models/organization_model.js @@ -2,11 +2,11 @@ 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 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 + 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. */ @@ -15,28 +15,28 @@ const knex = require('../utils/knex_config'); /** * Create Organization object - * @param {*} name - * @param {*} location - * @param {*} description - * @param {*} is_hiring - * @returns + * @param {*} name + * @param {*} location + * @param {*} description + * @param {*} is_hiring + * @returns */ -function organization(name, location, description, is_hiring){ - const organization = { - name: name, - location: location, - description: description, - is_hiring: is_hiring - }; - return organization; +function organization (name, location, description, is_hiring) { + const organization = { + name, + location, + description, + is_hiring + }; + return organization; } /** * Gets an Organization by its identifier - * @param {*} id - * @returns + * @param {*} id + * @returns */ -async function getOrganizationById(id){ +async function getOrganizationById (id) { const organization = await knex('Organization') .where('id', id) .select('*') @@ -46,69 +46,69 @@ async function getOrganizationById(id){ /** * Insert an Organization and its relative Administrator - * @param {*} organization + * @param {*} organization */ -async function insertOrganization(organization, organizationAdministratorId){ - 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(organization, '*'); - - // Inserting in the "OrganizationAdministrator" table - await trx('OrganizationAdministrator') - .insert({ - id_person: organizationAdministratorId, - id_organization: organizationResult[0].id, - }); +async function insertOrganization (organization, organizationAdministratorId) { + 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(organization, '*'); + + // Inserting in the "OrganizationAdministrator" table + await trx('OrganizationAdministrator') + .insert({ + id_person: organizationAdministratorId, + id_organization: organizationResult[0].id + }); }); } /** * Updates an Organization specified by the OrganizationId, if and * only if the specified personId is one of its Administrator - * @param {*} organization - * @param {*} organizationId - * @param {*} personId + * @param {*} organization + * @param {*} organizationId + * @param {*} personId * @returns true if the row was updated, false otherwise */ -async function updateOrganizationIfAdministrator(organization, organizationId, personId){ - // // 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 - // // }); +async function updateOrganizationIfAdministrator (organization, organizationId, personId) { + // // 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 numberOfUpdatedRows = await knex('Organization') .where('id', organizationId) - .whereExists(function(){ + .whereExists(function () { this.select('*') .from('OrganizationAdministrator') .where('id_person', personId) - .where('id_organization', organizationId) + .where('id_organization', organizationId); }) .update(organization); return numberOfUpdatedRows == 1; @@ -121,14 +121,14 @@ async function updateOrganizationIfAdministrator(organization, organizationId, p * @param {*} personId PersonId of the supposedly administrator * @returns true if the Organization was successfully deleted, false otherwise */ -async function deleteOrganizationIfAdmin(organizationId, personId){ +async function deleteOrganizationIfAdmin (organizationId, personId) { const numberOfDeletedRows = await knex('Organization') .where({ id: organizationId }) - .whereExists(function(){ + .whereExists(function () { this.select('*') .from('OrganizationAdministrator') .where('id_person', personId) - .where('id_organization', organizationId) + .where('id_organization', organizationId); }) .del(); return numberOfDeletedRows == 1; @@ -144,4 +144,4 @@ module.exports = { updateOrganizationIfAdministrator, updateOrganizationIfAdministrator, deleteOrganizationIfAdmin -}; \ No newline at end of file +}; diff --git a/backend/apis/nodejs/src/models/person_model.js b/backend/apis/nodejs/src/models/person_model.js index 1453142..5c5d6d9 100644 --- a/backend/apis/nodejs/src/models/person_model.js +++ b/backend/apis/nodejs/src/models/person_model.js @@ -2,11 +2,11 @@ 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 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 + 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. */ @@ -16,26 +16,26 @@ const bcrypt = require('bcrypt'); /** * Creates Person object by the specified fields - * @param {*} email - * @param {*} password - * @param {*} display_name - * @param {*} date_of_birth - * @param {*} available - * @param {*} enabled - * @param {*} place_of_living - * @returns + * @param {*} email + * @param {*} password + * @param {*} display_name + * @param {*} date_of_birth + * @param {*} available + * @param {*} enabled + * @param {*} place_of_living + * @returns */ -function person(email, password, display_name, date_of_birth, available, enabled, place_of_living) { - const person = { - email: email.toLowerCase(), - password: password, - display_name: display_name, - date_of_birth: date_of_birth, - available: available, - enabled: enabled, - place_of_living: place_of_living - }; - return person; +function person (email, password, display_name, date_of_birth, available, enabled, place_of_living) { + const person = { + email: email.toLowerCase(), + password, + display_name, + date_of_birth, + available, + enabled, + place_of_living + }; + return person; } /** @@ -43,22 +43,22 @@ function person(email, password, display_name, date_of_birth, available, enabled * @param {*} email email to look the Person for * @returns the Person object */ -async function getPersonByEmail(email){ - return await knex('Person') - .where('email', email.toLowerCase()) - .first(); +async function getPersonByEmail (email) { + return await knex('Person') + .where('email', email.toLowerCase()) + .first(); } /** * Get Person by Id * @param {*} id - The id to look the person for - * @returns + * @returns */ -async function getPersonById(id){ - return await knex('Person') - .select('*') - .where({ id: id }) - .first(); +async function getPersonById (id) { + return await knex('Person') + .select('*') + .where({ id }) + .first(); } /** @@ -67,27 +67,27 @@ async function getPersonById(id){ * @param {*} person A Person object * @param {*} activationLink the activationLink identifier */ -async function registerPerson(person, activationLink){ - // We need to insert either both in the "Person" table - // and in the "ActivationLink" one, or in neither - await knex.transaction(async (tr) => { - const personIdResult = await tr('Person') - .insert({ - email: person.email.toLowerCase(), - password: person.password, - display_name: person.display_name, - date_of_birth: person.date_of_birth, - available: person.available, - enabled: person.enabled, - place_of_living: person.place_of_living - }) - .returning("id"); - await tr('ActivationLink') - .insert({ - person_id: personIdResult[0].id, - identifier: activationLink - }); - }); +async function registerPerson (person, activationLink) { + // We need to insert either both in the "Person" table + // and in the "ActivationLink" one, or in neither + await knex.transaction(async (tr) => { + const personIdResult = await tr('Person') + .insert({ + email: person.email.toLowerCase(), + password: person.password, + display_name: person.display_name, + date_of_birth: person.date_of_birth, + available: person.available, + enabled: person.enabled, + place_of_living: person.place_of_living + }) + .returning('id'); + await tr('ActivationLink') + .insert({ + person_id: personIdResult[0].id, + identifier: activationLink + }); + }); } /** @@ -95,22 +95,22 @@ async function registerPerson(person, activationLink){ * Used for log-in * @param {*} email * @param {*} password - * @returns + * @returns */ -async function getPersonByEmailAndPassword(email, password){ - const person = await knex('Person') - .where('email', email.toLowerCase()) - .where('enabled', true) - .select('*') - .first(); +async function getPersonByEmailAndPassword (email, password) { + const person = await knex('Person') + .where('email', email.toLowerCase()) + .where('enabled', true) + .select('*') + .first(); - if(person){ - const passwordMatches = await bcrypt.compare(password, person.password); - if (passwordMatches) { - return person; - } + if (person) { + const passwordMatches = await bcrypt.compare(password, person.password); + if (passwordMatches) { + return person; } - return null; + } + return null; } /** @@ -118,32 +118,31 @@ async function getPersonByEmailAndPassword(email, password){ * @param {*} person The Person to update * @param {*} person_id The database id of the Person to update */ -async function updatePerson(person, person_id){ - await knex('Person') - .where('id', person_id) - .update(person); +async function updatePerson (person, person_id) { + await knex('Person') + .where('id', person_id) + .update(person); } /** * Deletes a Person specified by its database id. - * @param {*} person_id + * @param {*} person_id */ -async function deletePerson(person_id){ - await knex('Person') - .where({id : person_id}) - .del(); +async function deletePerson (person_id) { + await knex('Person') + .where({ id: person_id }) + .del(); } - // Exporting a function // means making a JavaScript function defined in one // module available for use in another module. module.exports = { - person, - getPersonByEmail, - getPersonById, - getPersonByEmailAndPassword, - registerPerson, - updatePerson, - deletePerson -}; \ No newline at end of file + person, + getPersonByEmail, + getPersonById, + getPersonByEmailAndPassword, + registerPerson, + updatePerson, + deletePerson +}; diff --git a/backend/apis/nodejs/src/routes/organization_admin_routes.js b/backend/apis/nodejs/src/routes/organization_admin_routes.js index a76fe36..e8325b0 100644 --- a/backend/apis/nodejs/src/routes/organization_admin_routes.js +++ b/backend/apis/nodejs/src/routes/organization_admin_routes.js @@ -2,11 +2,11 @@ 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 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 + 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. */ @@ -16,61 +16,57 @@ const organization_admin_model = require('../models/organization_admin_model'); /** * POST Method - * + * * Add an Administrator to an Organization. Allowed only if the * logged user is an Administrator themselves. - * + * * Required field(s): organization_id, person_id */ -async function addOrganizationAdmin(req, res){ +async function addOrganizationAdmin (req, res) { + // Ensure that the required fields are present before proceeding + if (!req.body.organization_id || !req.body.person_id) { + return res.status(400).json({ error: 'Invalid request' }); + } - // Ensure that the required fields are present before proceeding - if (!req.body.organization_id || !req.body.person_id) { - return res.status(400).json({ error : "Invalid request"}); - } - - try { - const isPersonAdmin = await organization_admin_model.isPersonAdmin(req.jwt.person_id, req.body.organization_id); - // TOC/TOU - if(!isPersonAdmin){ - return res.status(401).json({error : "Forbidden"}); - } - await organization_admin_model.addOrganizationAdministrator(req.body.person_id, req.body.organization_id); - return res.status(200).json({success : true}); - } - catch (error) { - console.error('Error while adding organization admin: ' + error); - res.status(500).json({error : "Internal server error"}); + try { + const isPersonAdmin = await organization_admin_model.isPersonAdmin(req.jwt.person_id, req.body.organization_id); + // TOC/TOU + if (!isPersonAdmin) { + return res.status(401).json({ error: 'Forbidden' }); } + await organization_admin_model.addOrganizationAdministrator(req.body.person_id, req.body.organization_id); + return res.status(200).json({ success: true }); + } catch (error) { + console.error('Error while adding organization admin: ' + error); + res.status(500).json({ error: 'Internal server error' }); + } } - - /** + +/** * DELETE Request - * + * * Deletes a Person from the list of Administrators of an Organization. * The logged user can only remove themselves. If no more Administrators * are left, the Organization is removed. - * + * * Required field(s): organization_id */ - async function removeOrganizationAdmin(req, res){ - - // Ensure that the required fields are present before proceeding - if (!req.body.organization_id) { - return res.status(400).json({ error : "Invalid request"}); - } - - try{ - await organization_admin_model.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id); - return res.status(200).json({success : true}); - } - catch (error){ - console.error(error); - return res.status(500).json({ error: "Internal server error"}); - } +async function removeOrganizationAdmin (req, res) { + // Ensure that the required fields are present before proceeding + if (!req.body.organization_id) { + return res.status(400).json({ error: 'Invalid request' }); + } + + try { + await organization_admin_model.removeOrganizationAdmin(req.jwt.person_id, req.body.organization_id); + return res.status(200).json({ success: true }); + } catch (error) { + console.error(error); + return res.status(500).json({ error: 'Internal server error' }); + } } module.exports = { addOrganizationAdmin, removeOrganizationAdmin -}; \ No newline at end of file +}; diff --git a/backend/apis/nodejs/src/routes/organization_post_routes.js b/backend/apis/nodejs/src/routes/organization_post_routes.js index 98ae01c..3efca8a 100644 --- a/backend/apis/nodejs/src/routes/organization_post_routes.js +++ b/backend/apis/nodejs/src/routes/organization_post_routes.js @@ -2,11 +2,11 @@ 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 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 + 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. */ @@ -15,61 +15,58 @@ const knex = require('../utils/knex_config'); /** * POST Request - * + * * Creates a Post belonging to an organization * * Required field(s): organization_id, content - * @returns the inserted Post + * @returns the inserted Post */ -async function createOrganizationPost(req, res){ +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' }); + } - // 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') + 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(); - - // 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') + + // 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 while creating Organization Post: " + error); - return res.status(500).json({error : "Internal server error"}); - } + return res.status(200).json(organizationPost[0]); + } catch (error) { + console.log('Error while creating Organization Post: ' + error); + return res.status(500).json({ error: 'Internal server error' }); } +} /** * DELETE Request - * + * * Deletes a Post belonging to an Organization, only if * the logged user is an administrator of that Organization. - * + * * Required field(s): none. */ -async function deleteOrganizationPost(req, res){ - +async function deleteOrganizationPost (req, res) { const organizationPostIdToDelete = req.params.id; - try{ + try { const isOrganizationAdmin = await knex('OrganizationPost') .join('OrganizationAdministrator', 'OrganizationPost.organization_id', 'OrganizationAdministrator.id_organization') .where('OrganizationPost.id', organizationPostIdToDelete) @@ -77,20 +74,18 @@ async function deleteOrganizationPost(req, res){ .select('*') .first(); - // Unexploitable TOC/TOU - if(isOrganizationAdmin){ - await knex('OrganizationPost') - .where('id', organizationPostIdToDelete) - .del(); - return res.status(200).json({success : true}); - } - else{ - return res.status(401).json({error : "Forbidden"}); - } - } - catch (error) { + // Unexploitable TOC/TOU + if (isOrganizationAdmin) { + await knex('OrganizationPost') + .where('id', organizationPostIdToDelete) + .del(); + return res.status(200).json({ success: true }); + } else { + return res.status(401).json({ error: 'Forbidden' }); + } + } catch (error) { console.log(error); - res.status(500).json({error : "Internal server error"}); + res.status(500).json({ error: 'Internal server error' }); } } @@ -98,6 +93,6 @@ async function deleteOrganizationPost(req, res){ // means making a JavaScript function defined in one // module available for use in another module. module.exports = { - createOrganizationPost, - deleteOrganizationPost -}; \ No newline at end of file + createOrganizationPost, + deleteOrganizationPost +}; diff --git a/backend/apis/nodejs/src/routes/organization_routes.js b/backend/apis/nodejs/src/routes/organization_routes.js index 3f64f42..7ef8086 100644 --- a/backend/apis/nodejs/src/routes/organization_routes.js +++ b/backend/apis/nodejs/src/routes/organization_routes.js @@ -2,11 +2,11 @@ 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 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 + 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. */ @@ -15,120 +15,111 @@ const organization_model = require('../models/organization_model'); /** * POST Request - * + * * Creates an Organization and its Administrator. - * + * * Required field(s): name * * @returns the inserted organization */ -async function createOrganization(req, res){ - +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"}); + return res.status(400).json({ error: 'Invalid request' }); } - try{ + try { const organization = organization_model.organization(req.body.name, req.body.location, req.body.description, req.body.is_hiring); await organization_model.insertOrganization(organization, req.jwt.person_id); return res.status(200).json({ Organization: organization }); - } - catch (error){ + } catch (error) { console.error('Error creating Organization:', error); - res.status(500).json({error : "Internal server 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){ - +async function updateOrganization (req, res) { const updateOrganization = {}; - if(req.body.name){ + if (req.body.name) { updateOrganization.name = req.body.name; } - if(req.body.location){ + if (req.body.location) { updateOrganization.location = req.body.location; } - if(req.body.description){ + if (req.body.description) { updateOrganization.description = req.body.description; } - if(req.body.is_hiring){ + 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"}); + return res.status(400).json({ error: 'Bad request. No data to update' }); } try { const isUpdateSuccessful = organization_model.updateOrganizationIfAdministrator(updateOrganization, req.params.id, req.jwt.person_id); - if(isUpdateSuccessful){ - return res.status(200).json({ success : "true"}); + if (isUpdateSuccessful) { + return res.status(200).json({ success: 'true' }); + } else { + return res.status(404).json({ error: 'Organization either not found or insufficient permissions' }); } - else{ - return res.status(404).json({error : "Organization either not found or insufficient permissions"}); - } - } - catch (error) { + } catch (error) { console.log(error); - return res.status(500).json({error : "Internal server 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 + * one of its administrator */ -async function deleteOrganization(req, res){ +async function deleteOrganization (req, res) { try { const isDeleteSuccessful = organization_model.deleteOrganizationIfAdmin(req.params.id, req.jwt.person_id); - if(isDeleteSuccessful){ - return res.status(403).json({error: "Forbidden"}); + if (isDeleteSuccessful) { + return res.status(403).json({ error: 'Forbidden' }); + } else { + return res.status(200).json({ success: true }); } - else{ - return res.status(200).json({success: true}); - } - } - catch (error) { + } catch (error) { console.error(error); - return res.status(500).json({error : "Internal server 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){ +async function getOrganization (req, res) { try { const organization = await organization_model.getOrganizationById(req.params.id); - if(organization) { + if (organization) { return res.status(200).json(organization); + } else { + return res.status(404).json({ error: 'Not found' }); } - 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"}); + } catch (error) { + console.error('Error retrieving an organization: ' + error); + return res.status(500).json({ error: 'Internal server error' }); } } @@ -138,4 +129,3 @@ module.exports = { updateOrganization, deleteOrganization }; - diff --git a/backend/apis/nodejs/src/routes/person_routes.js b/backend/apis/nodejs/src/routes/person_routes.js index cd6f270..eff1261 100644 --- a/backend/apis/nodejs/src/routes/person_routes.js +++ b/backend/apis/nodejs/src/routes/person_routes.js @@ -2,11 +2,11 @@ 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 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 + 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. */ @@ -22,23 +22,22 @@ const person_model = require('../models/person_model'); * POST Request * * Registers a Person - * + * * Required field(s): name, email (valid ISO standard), password - * + * * @returns The activationlink identifier */ -async function registerPerson(req, res){ - +async function registerPerson (req, res) { // Does this server allow users to register? - if (process.env.ALLOW_USER_REGISTRATION === 'false'){ - return res.status(403).json({error : "Users cannot register on this server"}); + if (process.env.ALLOW_USER_REGISTRATION === 'false') { + return res.status(403).json({ error: 'Users cannot register on this server' }); } // Ensure that the required fields are present before proceeding if (!req.body.display_name || !req.body.email || !req.body.password) { - return res.status(400).json({ error : "Some or all required fields are missing"}); + return res.status(400).json({ error: 'Some or all required fields are missing' }); } - if(!validator.validateEmail(req.body.email)){ - return res.status(400).json({ error : "The email is not in a valid format"}); + if (!validator.validateEmail(req.body.email)) { + return res.status(400).json({ error: 'The email is not in a valid format' }); } // Generate activation link token @@ -46,14 +45,14 @@ async function registerPerson(req, res){ // Hash provided password const hashPasswordPromise = bcrypt.hash(req.body.password, 10); - try{ + try { // Check whether e-mail exists already (enforced by database constraints) const existingUser = await person_model.getPersonByEmail(req.body.email); - if(existingUser){ - return res.status(409).json({ error: "E-mail already in use" }); + if (existingUser) { + return res.status(409).json({ error: 'E-mail already in use' }); } const personToInsert = person_model.person( - req.body.email, + req.body.email, await hashPasswordPromise, req.body.display_name, req.body.date_of_birth, @@ -61,43 +60,40 @@ async function registerPerson(req, res){ true, req.body.place_of_living); await person_model.registerPerson(personToInsert, activationLink); - return res.status(200).json({ activationLink: activationLink }); - } - catch (error){ + return res.status(200).json({ activationLink }); + } catch (error) { console.error('Error registering person:', error); - res.status(500).json({error : "Internal server error"}); + res.status(500).json({ error: 'Internal server error' }); } } /** * POST Request - * + * * Creates a token if the specified email * and password are valid. - * + * * Required field(s): email, password * * @returns The token */ -async function login(req, res){ - +async function login (req, res) { // Ensure that the required fields are present before proceeding if (!req.body.email || !req.body.password) { - return res.status(400).json({error : "Invalid request"}); + return res.status(400).json({ error: 'Invalid request' }); } - try{ + try { const person = await person_model.getPersonByEmailAndPassword(req.body.email, req.body.password); - if (person){ + if (person) { const token = jwt_utils.generateToken(person.id); - res.status(200).json({token: token }); + res.status(200).json({ token }); + } else { + res.status(401).json({ error: 'Unauthorized' }); } - else{ - res.status(401).json({error : "Unauthorized"}); - } - } catch(error){ + } catch (error) { console.error('Error logging in: ', error); - res.status(500).json({error : "Internal server error"}); + res.status(500).json({ error: 'Internal server error' }); } } @@ -105,138 +101,131 @@ async function login(req, res){ * Obtain a Person's details if the * Person to retrieve is either myself or an * enabled Person. - * + * * Required field(s): none * * @returns The Person */ -async function getPerson(req, res){ +async function getPerson (req, res) { try { const person = await person_model.getPersonById(req.params.id); - if(person){ + if (person) { // I am retrieving either myself or an enabled user - if(person.id == req.jwt.person_id || person.enabled){ - delete person['password']; // remove password field for security reasons + if (person.id == req.jwt.person_id || person.enabled) { + delete person.password; // remove password field for security reasons return res.status(200).send(person); } } - return res.status(404).json({error: "Not found"}); - } - catch (error) { - console.log("Error while getting person: " + error); - return res.status(500).json({error : "Internal server error"}); + return res.status(404).json({ error: 'Not found' }); + } catch (error) { + console.log('Error while getting person: ' + error); + return res.status(500).json({ error: 'Internal server error' }); } } /** - * + * * GET Request - * + * * Get myself, from the JWT token * * @returns Person's details */ -async function getMyself(req, res){ - try{ +async function getMyself (req, res) { + try { const person = await person_model.getPersonById(req.jwt.person_id); - if(person){ - delete person['password']; + if (person) { + delete person.password; return res.status(200).send(person); } - return res.status(404).json({error: "Not found"}); - } - catch (error){ - console.log("Error while getting myself: " + error); - return res.status(500).json({error : "Internal server error"}); + return res.status(404).json({ error: 'Not found' }); + } catch (error) { + console.log('Error while getting myself: ' + error); + return res.status(500).json({ error: 'Internal server error' }); } } /** * PUT request - * + * * Updates a Person's details. If some details are * not present, they shall be ignored. - * + * * Required field(s): none. Both old_password and * new_password if updating the password. * */ -async function updatePerson(req, res){ - - if (req.jwt.person_id != req.params.id){ - return res.status(403).json({ error : "Forbidden"}); +async function updatePerson (req, res) { + if (req.jwt.person_id != req.params.id) { + return res.status(403).json({ error: 'Forbidden' }); } const updatePerson = {}; - if(req.body.display_name){ + if (req.body.display_name) { updatePerson.display_name = req.body.display_name; } - if(req.body.date_of_birth){ - if(validator.isPostgresDateFormatValid(req.body.date_of_birth)){ + if (req.body.date_of_birth) { + if (validator.isPostgresDateFormatValid(req.body.date_of_birth)) { updatePerson.date_of_birth = req.body.date_of_birth; - } - else{ - return res.status(400).json({ error : "Date of birth format not valid. Please specify a YYYY-MM-DD date"}); + } else { + return res.status(400).json({ error: 'Date of birth format not valid. Please specify a YYYY-MM-DD date' }); } } - if(req.body.available){ + if (req.body.available) { updatePerson.available = req.body.available; } - if(req.body.place_of_living){ + if (req.body.place_of_living) { updatePerson.place_of_living = req.body.place_of_living; } - + // If we are tying to change password, the old password must be provided - if(req.body.old_password && req.body.new_password){ + if (req.body.old_password && req.body.new_password) { const user = await knex('Person') .select('password') .where({ id: req.jwt.person_id }) .first(); - const passwordMatches = await bcrypt.compare(req.body.old_password, user.password); - if(passwordMatches){ - updatePerson.password = await bcrypt.hash(req.body.new_password, 10); - } - else{ - return res.status(401).json({ error : "Password verification failed"}); - } + const passwordMatches = await bcrypt.compare(req.body.old_password, user.password); + if (passwordMatches) { + updatePerson.password = await bcrypt.hash(req.body.new_password, 10); + } else { + return res.status(401).json({ error: 'Password verification failed' }); + } } if (Object.keys(updatePerson).length === 0) { - return res.status(400).json({ error : "Bad request. No data to update"}); + return res.status(400).json({ error: 'Bad request. No data to update' }); } try { await person_model.updatePerson(updatePerson, req.params.id); - return res.status(200).json({ success : "true"}); - } - catch (error) { - console.log("Error while updating a Person: " + error); - return res.status(500).json({ error : "Internal server error"}); + return res.status(200).json({ success: 'true' }); + } catch (error) { + console.log('Error while updating a Person: ' + error); + return res.status(500).json({ error: 'Internal server error' }); } } /** * GET Request - * - * Deletes a Person. An user can only delete + * + * Deletes a Person. An user can only delete * themselves. - * + * * Required field(s): none * */ -async function deletePerson(req, res) { +async function deletePerson (req, res) { // TODO: Delete Organization if this user was its only administrator try { await person_model.deletePerson(req.jwt.person_id); - return res.status(200).json({success: true}); - } - catch (error) { - console.log("Error deleting a Person: " + error); - return res.status(500).json({error : "Internal server error"}); + return res.status(200).json({ success: true }); + } catch (error) { + console.log('Error deleting a Person: ' + error); + return res.status(500).json({ error: 'Internal server error' }); } } @@ -244,10 +233,10 @@ async function deletePerson(req, res) { // means making a JavaScript function defined in one // module available for use in another module. module.exports = { - registerPerson, - login, - getPerson, - getMyself, - updatePerson, - deletePerson -}; \ No newline at end of file + registerPerson, + login, + getPerson, + getMyself, + updatePerson, + deletePerson +}; diff --git a/backend/apis/nodejs/src/utils/jwt_utils.js b/backend/apis/nodejs/src/utils/jwt_utils.js index 4d138fa..8c685b8 100644 --- a/backend/apis/nodejs/src/utils/jwt_utils.js +++ b/backend/apis/nodejs/src/utils/jwt_utils.js @@ -2,50 +2,50 @@ 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 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 + 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 jwt = require('jsonwebtoken'); -function generateToken(person_id) { - // The payload the JWT will carry within itself - const payload = { - person_id: person_id - }; - - const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, { - expiresIn: '8h' - }); - return token; - } - - // Middlware - function verifyToken(req, res, next) { - const token = req.headers.authorization; - - if (!token) { - return res.status(401).send({error : 'No token provided'}); - } - - jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => { - if (err) { - return res.status(401).send({error : 'Failed to authenticate token'}); - } - - // If the token is valid, store the decoded data in the request object - // req.jwt will contain the payload created in generateToken - req.jwt = decoded; - next(); - }); +function generateToken (person_id) { + // The payload the JWT will carry within itself + const payload = { + person_id + }; + + const token = jwt.sign(payload, process.env.JWT_SECRET_KEY, { + expiresIn: '8h' + }); + return token; +} + +// Middlware +function verifyToken (req, res, next) { + const token = req.headers.authorization; + + if (!token) { + return res.status(401).send({ error: 'No token provided' }); } - module.exports = { - generateToken, - verifyToken -}; \ No newline at end of file + jwt.verify(token, process.env.JWT_SECRET_KEY, (err, decoded) => { + if (err) { + return res.status(401).send({ error: 'Failed to authenticate token' }); + } + + // If the token is valid, store the decoded data in the request object + // req.jwt will contain the payload created in generateToken + req.jwt = decoded; + next(); + }); +} + +module.exports = { + generateToken, + verifyToken +}; diff --git a/backend/apis/nodejs/src/utils/knex_config.js b/backend/apis/nodejs/src/utils/knex_config.js index 25ac3cf..b851d90 100644 --- a/backend/apis/nodejs/src/utils/knex_config.js +++ b/backend/apis/nodejs/src/utils/knex_config.js @@ -2,24 +2,24 @@ 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 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 + 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 knexInstance = require('knex')({ - client: 'pg', - connection: { - host: process.env.POSTGRES_SERVER, - user: process.env.POSTGRES_USERNAME, - password: process.env.POSTGRES_PASSWORD, - port: process.env.POSTGRES_PORT, - database: 'Blink' - } - }); + client: 'pg', + connection: { + host: process.env.POSTGRES_SERVER, + user: process.env.POSTGRES_USERNAME, + password: process.env.POSTGRES_PASSWORD, + port: process.env.POSTGRES_PORT, + database: 'Blink' + } +}); - module.exports = knexInstance; \ No newline at end of file +module.exports = knexInstance; diff --git a/backend/apis/nodejs/src/utils/validation.js b/backend/apis/nodejs/src/utils/validation.js index 4b6fc34..355ca1e 100644 --- a/backend/apis/nodejs/src/utils/validation.js +++ b/backend/apis/nodejs/src/utils/validation.js @@ -2,37 +2,37 @@ 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 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 + 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. */ /** * Checks whether an e-mail is in a valid format - * @param {*} email email to validate + * @param {*} email email to validate * @returns true or false */ -function validateEmail(email) { - const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/; - return regex.test(email); +function validateEmail (email) { + const regex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/; + return regex.test(email); } - + /** * Checks whether a date is in a correct Postgres * format (YYYY-MM-DD) - * @param {*} dateString the date to validate + * @param {*} dateString the date to validate * @returns true or false */ -function isPostgresDateFormatValid(dateString) { - const regex = /^\d{4}-\d{2}-\d{2}$/; - return regex.test(dateString); +function isPostgresDateFormatValid (dateString) { + const regex = /^\d{4}-\d{2}-\d{2}$/; + return regex.test(dateString); } module.exports = { - validateEmail, - isPostgresDateFormatValid -}; \ No newline at end of file + validateEmail, + isPostgresDateFormatValid +}; diff --git a/backend/apis/nodejs/tests/person.test.js b/backend/apis/nodejs/tests/person.test.js index 8710f90..2d751b0 100644 --- a/backend/apis/nodejs/tests/person.test.js +++ b/backend/apis/nodejs/tests/person.test.js @@ -7,24 +7,24 @@ require('dotenv').config({ path: '../src/.env' }); describe('Person Tests', () => { test('Correct registration', async () => { const response = await request(app) - .post('/api/register') - .send({ - email : "johntestdoe@mail.org", - password : "password", - display_name : "John Doe" - }) + .post('/api/register') + .send({ + email: 'johntestdoe@mail.org', + password: 'password', + display_name: 'John Doe' + }); expect(response.status).toBe(200); expect(response.body).toEqual({ activationLink: expect.any(String) }); }); test('Incorrect registration', async () => { const response = await request(app) - .post('/api/register') - .send({ - email : "this is not an email", - password : "password", - display_name : "John Doe" - }) + .post('/api/register') + .send({ + email: 'this is not an email', + password: 'password', + display_name: 'John Doe' + }); expect(response.status).toBe(400); }); }); diff --git a/frontend/vanilla/constants.js b/frontend/vanilla/constants.js index 06efcb3..1a47f0c 100644 --- a/frontend/vanilla/constants.js +++ b/frontend/vanilla/constants.js @@ -1 +1 @@ -const apiUrl = "http://localhost:3000/blinkapi"; \ No newline at end of file +const apiUrl = 'http://localhost:3000/blinkapi'; diff --git a/tutorials/0_callbacks.js b/tutorials/0_callbacks.js index f57d5ed..b66ee29 100644 --- a/tutorials/0_callbacks.js +++ b/tutorials/0_callbacks.js @@ -1,35 +1,32 @@ // https://javascript.info/callbacks - function execute_action(param, callback){ - - if(param == "something"){ - console.log("Executing action: " + param); - callback(null, Date.now()); - } - else{ - // We can call callback with one argument even if - // the signature states two parameters - callback(new Error("Invalid parameter")) - } +function execute_action (param, callback) { + if (param == 'something') { + console.log('Executing action: ' + param); + callback(null, Date.now()); + } else { + // We can call callback with one argument even if + // the signature states two parameters + callback(new Error('Invalid parameter')); + } } -function entryPoint(){ - /* ===== Begin Simple callback ===== */ +function entryPoint () { + /* ===== Begin Simple callback ===== */ - execute_action("something", function (error, time_of_completion){ - if(error){ - console.log("Something happened"); - } - else{ - console.log("Time of completion: " + new Date(time_of_completion).toDateString()); - } - }); - console.log("I started here!"); - /* - Ciò è utile se ad esempio execute_action fa operazioni lente (ad esempio + execute_action('something', function (error, time_of_completion) { + if (error) { + console.log('Something happened'); + } else { + console.log('Time of completion: ' + new Date(time_of_completion).toDateString()); + } + }); + console.log('I started here!'); + /* + Ciò è utile se ad esempio execute_action fa operazioni lente (ad esempio scrittura su database, connessioni HTTP ecc..) ma abbiamo bisogno del suo valore di ritorno per continuare una determinata operazione - (in questo caso la data di completamento dell'esecuzione), + (in questo caso la data di completamento dell'esecuzione), senza però bloccare le altre operazioni che non hanno bisogno di tale valore, in questo caso console.log("I started here!"); @@ -37,12 +34,12 @@ function entryPoint(){ eseguite in maniera sincrona, ma che ci permette di comprendere le basi di questo meccanismo. */ - - /* ===== End Simple Callback ===== */ + + /* ===== End Simple Callback ===== */ } entryPoint(); // callbacks usually indicate that the // execution of their code will continue -// asynchronously. \ No newline at end of file +// asynchronously. diff --git a/tutorials/1_promises.js b/tutorials/1_promises.js index 9cbd4fa..2870895 100644 --- a/tutorials/1_promises.js +++ b/tutorials/1_promises.js @@ -10,24 +10,24 @@ Remember that Promises are not intrensically asyncronous */ -let promise = new Promise(function(resolve, reject) { - setTimeout(() => resolve("done"), 500); - }); +const promise = new Promise(function (resolve, reject) { + setTimeout(() => resolve('done'), 500); +}); /* The first argument of .then is a function that runs when the promise is resolved and receives the result. The second argument of .then is a function that runs when the promise is rejected and receives the error. */ promise.then( - result => console.log("The operation was successful. It returned " + result), - error => console.log("The operation was not successful: " + error) + result => console.log('The operation was successful. It returned ' + result), + error => console.log('The operation was not successful: ' + error) ); /* Or we can pass only one argument if we're interested only in a positive result */ promise.then( - result => console.log("The operation was successful. It returned " + result) + result => console.log('The operation was successful. It returned ' + result) ); /* @@ -37,12 +37,12 @@ promise.then( promise.catch internally just calls promise.then(null, f) */ promise.catch( - error => console.log(error) + error => console.log(error) ); /* finally gets always called */ promise.finally( - () => console.log("The execution has terminated. Bye") -); \ No newline at end of file + () => console.log('The execution has terminated. Bye') +); diff --git a/tutorials/2_promise_chaining.js b/tutorials/2_promise_chaining.js index ed2551d..b5b8c28 100644 --- a/tutorials/2_promise_chaining.js +++ b/tutorials/2_promise_chaining.js @@ -3,29 +3,21 @@ // .then() returns a new Promise when you do "return", // internally calling resolve(). -new Promise(function(resolve, reject) { - - setTimeout(() => resolve(1), 1); - - }).then(function(result) { - - console.log(result); - return result * 2; - - }).then(function(result) { - - console.log(result); - return result * 2; - - }).then(function(result) { - - console.log(result); - return result * 2; - - }); +new Promise(function (resolve, reject) { + setTimeout(() => resolve(1), 1); +}).then(function (result) { + console.log(result); + return result * 2; +}).then(function (result) { + console.log(result); + return result * 2; +}).then(function (result) { + console.log(result); + return result * 2; +}); /* - It will print + It will print 1 2 4 @@ -33,7 +25,7 @@ new Promise(function(resolve, reject) { /* It means that "then" is internally implemented roughly as follows: - + function then(f){ return new Promise(function(resolve, reject) { resolve(f()); @@ -45,12 +37,12 @@ new Promise(function(resolve, reject) { fetch('http://www.fsf.org') // .then below runs when the remote server responds - .then(function(response) { + .then(function (response) { // response.text() returns a new promise that resolves with the full response text // when it loads return response.text(); }) - .then(function(text) { + .then(function (text) { // ...and here's the content of the remote file console.log(text); - }); \ No newline at end of file + }); diff --git a/tutorials/3_async-await.js b/tutorials/3_async-await.js index e425193..e05074d 100644 --- a/tutorials/3_async-await.js +++ b/tutorials/3_async-await.js @@ -3,63 +3,62 @@ // A async function always returns a promise. Other values are wrapped in a resolved promise automatically. // Async e Await sono solo "sintassi zuccherina" per rendere l'utilizzo delle Promise più semplice -async function f1() { - return 1; - } - - f1().then(console.log); // 1 +async function f1 () { + return 1; +} + +f1().then(console.log); // 1 // The keyword await makes JavaScript wait until that promise settles and returns its result. // It can be used in async functions only -// Let’s emphasize: await literally suspends the function execution until the promise settles, +// Let’s emphasize: await literally suspends the function execution until the promise settles, // and then resumes it with the promise result. -async function f2() { - - let promise = new Promise((resolve, reject) => { - setTimeout(() => resolve("done!"), 1000) +async function f2 () { + const promise = new Promise((resolve, reject) => { + setTimeout(() => resolve('done!'), 1000); }); - - let result = await promise; // wait until the promise resolves (*) - + + const result = await promise; // wait until the promise resolves (*) + console.log(result); // "done!" } - - f2(); - // Tutto il codice nella stessa funzione (o nello stesso blocco di codice) - // dopo "await" è da considerarsi nel "then()" di una promessa. Pertanto dopo - // await (ma prima del completamento della Promise), - // il flusso di esecuzione va fuori a quel blocco di codice. Ad esempio considera - // il seguente esempio: - async function exampleAsyncFunction() { - console.log('Before await'); - await new Promise(function(resolve, reject) { - setTimeout(() => resolve("done"), 500); - }); // Pauses execution here until the promise resolves. - console.log('After await'); - } - - console.log('Start'); - exampleAsyncFunction(); - console.log('End'); +f2(); - // Il risultato sarà: - // Start, Before Await, End, After await - // Viene prima "End" e poi "After Await", perché - // dopo await, il flusso di esecuzione ritorna al - // chiamante +// Tutto il codice nella stessa funzione (o nello stesso blocco di codice) +// dopo "await" è da considerarsi nel "then()" di una promessa. Pertanto dopo +// await (ma prima del completamento della Promise), +// il flusso di esecuzione va fuori a quel blocco di codice. Ad esempio considera +// il seguente esempio: +async function exampleAsyncFunction () { + console.log('Before await'); + await new Promise(function (resolve, reject) { + setTimeout(() => resolve('done'), 500); + }); // Pauses execution here until the promise resolves. + console.log('After await'); +} - // Domande - // - // Why await only works in async function in javascript? - // https://stackoverflow.com/questions/49640647/why-await-only-works-in-async-function-in-javascript - // - // Why using async-await - // https://stackoverflow.com/questions/42624647/why-use-async-when-i-have-to-use-await - // - // Si faccia presente che non è possibile creare da zero una funzione asincrona (come - // setInterval o fetch) - // https://stackoverflow.com/questions/61857274/how-to-create-custom-asynchronous-function-in-javascript - // - // Altra domanda interessante - // https://stackoverflow.com/questions/42624647/why-use-async-when-i-have-to-use-await +console.log('Start'); +exampleAsyncFunction(); +console.log('End'); + +// Il risultato sarà: +// Start, Before Await, End, After await +// Viene prima "End" e poi "After Await", perché +// dopo await, il flusso di esecuzione ritorna al +// chiamante + +// Domande +// +// Why await only works in async function in javascript? +// https://stackoverflow.com/questions/49640647/why-await-only-works-in-async-function-in-javascript +// +// Why using async-await +// https://stackoverflow.com/questions/42624647/why-use-async-when-i-have-to-use-await +// +// Si faccia presente che non è possibile creare da zero una funzione asincrona (come +// setInterval o fetch) +// https://stackoverflow.com/questions/61857274/how-to-create-custom-asynchronous-function-in-javascript +// +// Altra domanda interessante +// https://stackoverflow.com/questions/42624647/why-use-async-when-i-have-to-use-await diff --git a/tutorials/delay.js b/tutorials/delay.js index 6b07f86..89853f7 100644 --- a/tutorials/delay.js +++ b/tutorials/delay.js @@ -3,10 +3,10 @@ The function delay(ms) should return a promise. That promise should resolve after ms milliseconds, so that we can add .then to it, like this: */ -function delay(ms){ - return new Promise(resolve => { - setTimeout(resolve, ms); - }); +function delay (ms) { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); } -delay(1000).then(() => console.log("Hello world!")); +delay(1000).then(() => console.log('Hello world!'));