diff --git a/backend/apis/nodejs/src/app.js b/backend/apis/nodejs/src/app.js index b87909b..ab8bca9 100644 --- a/backend/apis/nodejs/src/app.js +++ b/backend/apis/nodejs/src/app.js @@ -2,64 +2,65 @@ 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. */ // Importing modules // TODO: clean up -require('dotenv').config() -const express = require('express') -const cors = require('cors') -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') +require('dotenv').config(); +const express = require('express'); +const cors = require('cors'); +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 jwt_utils = require('./utils/jwt_utils.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 -app.use(cors()) // Enable CORS for all routes +const app = express(); +app.use(express.json()); // Middleware which parses JSON for POST requests +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' } -})) // Apply the rate limiter middleware to all routes + message: {error : "Too many requests from this IP, please try again later"} +})); // Apply the rate limiter middleware to all routes -const publicRoutes = express.Router() -publicRoutes.post('/register', personRoutes.registerPerson) -publicRoutes.post('/login', personRoutes.login) +const publicRoutes = express.Router(); +publicRoutes.post('/register', personRoutes.registerPerson); +publicRoutes.post('/login', personRoutes.login); -const protectedRoutes = express.Router() -protectedRoutes.use(jwt_utils.verifyToken) -protectedRoutes.get('/person/myself', personRoutes.getMyself) -protectedRoutes.get('/person/:id', personRoutes.getPerson) -protectedRoutes.put('/person/:id', personRoutes.updatePerson) -protectedRoutes.delete('/person/delete', personRoutes.deletePerson) -protectedRoutes.post('/organization/admin', organizationAdminRoutes.addOrganizationAdmin) -protectedRoutes.delete('/organization/removeadmin', organizationAdminRoutes.removeOrganizationAdmin) -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', organizationPostRoutes.createOrganizationPost) -protectedRoutes.delete('/organization/post/:id', organizationPostRoutes.deleteOrganizationPost) +const protectedRoutes = express.Router(); +protectedRoutes.use(jwt_utils.verifyToken); +protectedRoutes.get('/person/myself', personRoutes.getMyself); +protectedRoutes.get('/person/:id', personRoutes.getPerson); +protectedRoutes.put('/person/:id', personRoutes.updatePerson); +protectedRoutes.delete('/person/delete', personRoutes.deletePerson); +protectedRoutes.post('/organization/admin', organizationAdminRoutes.addOrganizationAdmin); +protectedRoutes.delete('/organization/removeadmin', organizationAdminRoutes.removeOrganizationAdmin); +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', organizationPostRoutes.createOrganizationPost); +protectedRoutes.delete('/organization/post/:id', organizationPostRoutes.deleteOrganizationPost); // Mounting routes -app.use('/api', publicRoutes) // Routes not requiring token -app.use('/api', protectedRoutes) // Routes requiring token +app.use('/api', publicRoutes); // Routes not requiring token +app.use('/api', protectedRoutes); // Routes requiring token // Start the server. Default port is 3000 -const port = process.env.API_SERVER_PORT || 3000 +const port = process.env.API_SERVER_PORT || 3000; app.listen(port, () => { - console.log(`Blink API server is running on port ${port}`) -}) + console.log(`Blink API server is running on port ${port}`); +}); -module.exports = app +module.exports = app; \ No newline at end of file diff --git a/backend/apis/nodejs/src/models/organization_model.js b/backend/apis/nodejs/src/models/organization_model.js index e3b53b1..0574565 100644 --- a/backend/apis/nodejs/src/models/organization_model.js +++ b/backend/apis/nodejs/src/models/organization_model.js @@ -2,116 +2,116 @@ 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 knex = require('../utils/knex_config') +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, - location, - description, - is_hiring - } - return organization +function organization(name, location, description, is_hiring){ + const organization = { + name: name, + location: location, + description: description, + is_hiring: 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('*') - .first() - return organization + .first(); + return organization; } /** * 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) }) - .update(organization) - return numberOfUpdatedRows == 1 + .update(organization); + return numberOfUpdatedRows == 1; } /** @@ -121,17 +121,17 @@ async function updateOrganizationIfAdministrator (organization, organizationId, * @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) }) - .del() - return numberOfDeletedRows == 1 + .del(); + return numberOfDeletedRows == 1; } // Exporting a function @@ -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 05b8a43..1453142 100644 --- a/backend/apis/nodejs/src/models/person_model.js +++ b/backend/apis/nodejs/src/models/person_model.js @@ -2,40 +2,40 @@ 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 knex = require('../utils/knex_config') -const bcrypt = require('bcrypt') +const knex = require('../utils/knex_config'); +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, - display_name, - date_of_birth, - available, - enabled, - 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: password, + display_name: display_name, + date_of_birth: date_of_birth, + available: available, + enabled: enabled, + place_of_living: place_of_living + }; + return person; } /** @@ -43,22 +43,22 @@ function person (email, password, display_name, date_of_birth, available, enable * @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 }) - .first() +async function getPersonById(id){ + return await knex('Person') + .select('*') + .where({ id: 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,31 +118,32 @@ 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 -} + person, + getPersonByEmail, + getPersonById, + getPersonByEmailAndPassword, + registerPerson, + updatePerson, + deletePerson +}; \ No newline at end of file diff --git a/backend/apis/nodejs/src/routes/organization_admin_routes.js b/backend/apis/nodejs/src/routes/organization_admin_routes.js index 90c0c3a..a76fe36 100644 --- a/backend/apis/nodejs/src/routes/organization_admin_routes.js +++ b/backend/apis/nodejs/src/routes/organization_admin_routes.js @@ -2,71 +2,75 @@ 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 knex = require('../utils/knex_config') -const organization_admin_model = require('../models/organization_admin_model') +const knex = require('../utils/knex_config'); +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) { - // 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' }) - } +async function addOrganizationAdmin(req, res){ - 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' }) + // 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"}); } - 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 3225dc5..98ae01c 100644 --- a/backend/apis/nodejs/src/routes/organization_post_routes.js +++ b/backend/apis/nodejs/src/routes/organization_post_routes.js @@ -2,90 +2,95 @@ 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 knex = require('../utils/knex_config') +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) { - // 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' }) - } +async function createOrganizationPost(req, res){ - try { - // Check if the current user is a organization's administrator - const isOrganizationAdmin = await knex('OrganizationAdministrator') + // 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() - - // 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') + .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') .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' }) + .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"}); + } } -} /** * 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) { - const organizationPostIdToDelete = req.params.id +async function deleteOrganizationPost(req, res){ - try { + const organizationPostIdToDelete = req.params.id; + + try{ const isOrganizationAdmin = await knex('OrganizationPost') .join('OrganizationAdministrator', 'OrganizationPost.organization_id', 'OrganizationAdministrator.id_organization') .where('OrganizationPost.id', organizationPostIdToDelete) .where('OrganizationAdministrator.id_person', req.jwt.person_id) .select('*') - .first() + .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) { - console.log(error) - res.status(500).json({ error: 'Internal server 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"}); } } @@ -93,6 +98,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 -} + createOrganizationPost, + deleteOrganizationPost +}; \ No newline at end of file diff --git a/backend/apis/nodejs/src/routes/organization_routes.js b/backend/apis/nodejs/src/routes/organization_routes.js index 0d1cf2d..3f64f42 100644 --- a/backend/apis/nodejs/src/routes/organization_routes.js +++ b/backend/apis/nodejs/src/routes/organization_routes.js @@ -2,124 +2,133 @@ 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 organization_model = require('../models/organization_model') +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 { - 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) { - console.error('Error creating Organization:', error) - res.status(500).json({ error: 'Internal server error' }) + 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){ + 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 = {} +async function updateOrganization(req, res){ - if (req.body.name) { - updateOrganization.name = req.body.name + const updateOrganization = {}; + + if(req.body.name){ + updateOrganization.name = req.body.name; } - if (req.body.location) { - updateOrganization.location = req.body.location + if(req.body.location){ + updateOrganization.location = req.body.location; } - if (req.body.description) { - updateOrganization.description = req.body.description + if(req.body.description){ + updateOrganization.description = req.body.description; } - if (req.body.is_hiring) { - updateOrganization.is_hiring = 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' }) - } else { - return res.status(404).json({ error: 'Organization either not found or insufficient permissions' }) + const isUpdateSuccessful = organization_model.updateOrganizationIfAdministrator(updateOrganization, req.params.id, req.jwt.person_id); + if(isUpdateSuccessful){ + return res.status(200).json({ success : "true"}); } - } catch (error) { - console.log(error) - return res.status(500).json({ error: 'Internal server error' }) + 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 + * 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' }) - } else { - return res.status(200).json({ success: true }) + const isDeleteSuccessful = organization_model.deleteOrganizationIfAdmin(req.params.id, req.jwt.person_id); + if(isDeleteSuccessful){ + return res.status(403).json({error: "Forbidden"}); } - } catch (error) { - console.error(error) - return res.status(500).json({ error: 'Internal server error' }) + 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) { +async function getOrganization(req, res){ try { - const organization = await organization_model.getOrganizationById(req.params.id) - if (organization) { - return res.status(200).json(organization) - } else { - return res.status(404).json({ error: 'Not found' }) + const organization = await organization_model.getOrganizationById(req.params.id); + if(organization) { + return res.status(200).json(organization); } - } catch (error) { - console.error('Error retrieving an organization: ' + error) - return res.status(500).json({ error: 'Internal server error' }) + 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"}); } } @@ -128,4 +137,5 @@ module.exports = { getOrganization, updateOrganization, deleteOrganization -} +}; + diff --git a/backend/apis/nodejs/src/routes/person_routes.js b/backend/apis/nodejs/src/routes/person_routes.js index 13230a2..cd6f270 100644 --- a/backend/apis/nodejs/src/routes/person_routes.js +++ b/backend/apis/nodejs/src/routes/person_routes.js @@ -2,98 +2,102 @@ 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 validator = require('../utils/validation') -const knex = require('../utils/knex_config') -const jwt_utils = require('../utils/jwt_utils') -const bcrypt = require('bcrypt') -const crypto = require('crypto') -const person_model = require('../models/person_model') +const validator = require('../utils/validation'); +const knex = require('../utils/knex_config'); +const jwt_utils = require('../utils/jwt_utils'); +const bcrypt = require('bcrypt'); +const crypto = require('crypto'); +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 - const activationLink = crypto.randomBytes(16).toString('hex') + const activationLink = crypto.randomBytes(16).toString('hex'); // Hash provided password - const hashPasswordPromise = bcrypt.hash(req.body.password, 10) + 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' }) + const existingUser = await person_model.getPersonByEmail(req.body.email); + 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, req.body.available, true, - req.body.place_of_living) - await person_model.registerPerson(personToInsert, activationLink) - return res.status(200).json({ activationLink }) - } catch (error) { - console.error('Error registering person:', error) - res.status(500).json({ error: 'Internal server error' }) + req.body.place_of_living); + await person_model.registerPerson(personToInsert, activationLink); + return res.status(200).json({ activationLink: activationLink }); + } + catch (error){ + console.error('Error registering person:', 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 { - const person = await person_model.getPersonByEmailAndPassword(req.body.email, req.body.password) - if (person) { - const token = jwt_utils.generateToken(person.id) - res.status(200).json({ token }) - } else { - res.status(401).json({ error: 'Unauthorized' }) + try{ + const person = await person_model.getPersonByEmailAndPassword(req.body.email, req.body.password); + if (person){ + const token = jwt_utils.generateToken(person.id); + res.status(200).json({token: token }); } - } catch (error) { - console.error('Error logging in: ', error) - res.status(500).json({ error: 'Internal server error' }) + else{ + res.status(401).json({error : "Unauthorized"}); + } + } catch(error){ + console.error('Error logging in: ', error); + res.status(500).json({error : "Internal server error"}); } } @@ -101,131 +105,138 @@ 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) { + const person = await person_model.getPersonById(req.params.id); + 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 - return res.status(200).send(person) + 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 { - const person = await person_model.getPersonById(req.jwt.person_id) - if (person) { - delete person.password - return res.status(200).send(person) +async function getMyself(req, res){ + try{ + const person = await person_model.getPersonById(req.jwt.person_id); + 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 = {} + const updatePerson = {}; - if (req.body.display_name) { - updatePerson.display_name = 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)) { - 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' }) + 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"}); } } - if (req.body.available) { - updatePerson.available = req.body.available + if(req.body.available){ + updatePerson.available = req.body.available; } - if (req.body.place_of_living) { - updatePerson.place_of_living = 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' }) - } + .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"}); + } } 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' }) + 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"}); } } /** * 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' }) + 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"}); } } @@ -233,10 +244,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 -} + registerPerson, + login, + getPerson, + getMyself, + updatePerson, + deletePerson +}; \ No newline at end of file diff --git a/backend/apis/nodejs/src/utils/jwt_utils.js b/backend/apis/nodejs/src/utils/jwt_utils.js index efafa55..4d138fa 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') +const jwt = require('jsonwebtoken'); -function generateToken (person_id) { - // The payload the JWT will carry within itself - const payload = { - person_id +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; } - - 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' }) + + // 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(); + }); + } - // 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 -} + module.exports = { + generateToken, + verifyToken +}; \ No newline at end of file diff --git a/backend/apis/nodejs/src/utils/knex_config.js b/backend/apis/nodejs/src/utils/knex_config.js index e4cc40d..25ac3cf 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 + module.exports = knexInstance; \ No newline at end of file diff --git a/backend/apis/nodejs/src/utils/validation.js b/backend/apis/nodejs/src/utils/validation.js index c21b790..4b6fc34 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 -} + validateEmail, + isPostgresDateFormatValid +}; \ No newline at end of file diff --git a/backend/apis/nodejs/tests/person.test.js b/backend/apis/nodejs/tests/person.test.js index a40b11c..8710f90 100644 --- a/backend/apis/nodejs/tests/person.test.js +++ b/backend/apis/nodejs/tests/person.test.js @@ -1,30 +1,30 @@ // Run me with "npm test" -const request = require('supertest') -const app = require('../src/app') -require('dotenv').config({ path: '../src/.env' }) +const request = require('supertest'); +const app = require('../src/app'); +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' - }) - expect(response.status).toBe(200) - expect(response.body).toEqual({ activationLink: expect.any(String) }) - }) + .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' - }) - expect(response.status).toBe(400) - }) -}) + .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 55498f7..06efcb3 100644 --- a/frontend/vanilla/constants.js +++ b/frontend/vanilla/constants.js @@ -1 +1 @@ -const apiUrl = 'http://localhost:3000/blinkapi' +const apiUrl = "http://localhost:3000/blinkapi"; \ No newline at end of file